原文: [WPF 容易忽視的細節] —— Exception in WPF's Converter
前言:
在WPF中,Converter是我們經常要用到的一個工具,因為XAML上綁定的資料不一定是我們需要的資料。
問題:
在Converter中抛出一個異常導緻程式崩潰,而且是在對未捕獲異常進行集中處理的情況。
補充:錯誤場景。
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestProject"
Title="MainWindow"
Width="525"
Height="350"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:ErrorConverter x:Key="ErrorConverter" />
</Window.Resources>
<Grid>
<TextBlock Text="{Binding Content, Converter={StaticResource ErrorConverter}}" />
</Grid>
</Window>
namespace TestProject
{
using System;
using System.Globalization;
using System.Windows.Data;
/// <summary>
/// 轉換過程中跑出異常的Converter。
/// </summary>
public class ErrorConverter : IValueConverter
{
/// <summary>
/// 隻抛出錯誤,不做任何錯誤。
/// </summary>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("Just for test! - In ErrorConverter - Convert");
}
/// <summary>
/// 隻抛出錯誤,不做任何錯誤。
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("Just for test! - In ErrorConverter - ConvertBack");
}
}
}
上面的場景中,當TextBlock綁定到Content時,便會觸發ErrorConverter的Convert方法,
但是Convert方法因為抛出了異常,導緻整個程式挂掉。
雖然,在App.xaml.cs中集中對未捕獲異常進行處理,但是卻無法捕獲這個異常。
public App()
{
this.DispatcherUnhandledException +=new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);
AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
探索過程:
1、UnhandledException
因為程式中,難免會出現沒有處理的異常,這些異常如果沒有被處理就會先被抛給我們的主程式,
如果還未被處理就會被抛給.Net Framework,最終導緻程式挂掉。
是以,我時常集中處理未被處理的這部分異常,以防止程式挂掉。
相關内容可以參考:
CSharp UnhandledException。
當捕獲到未被處理的異常時,我就會彈出一個消息框,然後列印到日志中。
理論上來說,這是可以捕獲所有程式中抛出的異常的,但是當Converter中抛出異常時,卻導緻程式崩潰。
通過Debug,導緻崩潰異常為StackOverflowException。
2、StackOverflowException
一方面這個異常的名字,是我已經去的一個網站;另外一方面這個異常是無法被捕捉的。
這是MSDN上的解釋:
StackOverflowException。
而且,我的經驗是一般出現這個異常就是說明程式中出現了死循環。
但是,當我檢查程式時發現,我并沒有寫此類代碼。
而且錯誤出現在MessageBox.Show("");這行。(就是我在主程式中處理未捕獲的異常時,抛出消息框)。
// 當捕獲未處理異常時
Exception ex = e.Exception;
const string errorMsg = "UI Thread Exception : \n\n";
MessageBox.Show("An unhandled UI Thread exception occurred");
Logger.Error(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
e.Handled = true;
将此行注釋掉,發現一切正常,程式不再挂掉。說明是MessageBox引起StackOverflowException。
3、 MessageBox
當MessageBox.Show("");時,會阻塞目前線程,是以理論上來說每次隻能顯示一個MessageBox。
// 點選按鈕執行以下代碼
// 隻有點選MessageBox上的确認按鈕才會顯示下一個,不會一次全部顯示。
for (int i = 0; i < 5; i++)
{
MessageBox.Show("Just For test");
}
說明應該是MessageBox.Show被多次調用,造成StackOverflowException。
4、 Logger
再去檢查日志,發現沒有日志資訊列印出來,說明應該是一直停留在MessageBox.Show這裡,
是以日志還沒來得及列印程式便挂掉了。
5、Trace Converter
有一個猜想,就是Converter被調用了多次:當xaml解析器發現Converter抛出異常時,
便再次調用Converter,導緻又一次抛出異常,主程式捕獲後,又執行MessageBox.Show,
是以程式挂掉了。
驗證猜想:
public class TestConverter : IValueConverter
{
// 臨時變量,用于記錄被調用的次數
private int temp = 0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// 列印出次數
using (var writer = File.AppendText("D:\\Log.txt"))
{
writer.WriteLine(DateTime.Now + " - " + temp++);
}
throw new Exception("Just for test!");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
再次運作錯誤的程式,不例外程式崩潰,但是我也得到了想要的資料。
9/12/2013 3:01:42 PM - 0
9/12/2013 3:01:42 PM - 1
9/12/2013 3:01:42 PM - 2
9/12/2013 3:01:42 PM - 3
9/12/2013 3:01:42 PM - 4
9/12/2013 3:01:42 PM - 5
9/12/2013 3:01:42 PM - 6
9/12/2013 3:01:42 PM - 7
9/12/2013 3:01:42 PM - 8
9/12/2013 3:01:42 PM - 9
9/12/2013 3:01:43 PM - 10
9/12/2013 3:01:43 PM - 11
9/12/2013 3:01:43 PM - 12
9/12/2013 3:01:43 PM - 13
9/12/2013 3:01:43 PM - 14
跟我猜想的一樣,但是Converter确被調用了15次,這也是導緻程式崩潰的原因。
結論:
1、 當MessageBox.Show被同時多次調用時,會出現StackOverflowException的異常。
這個我不知道如何驗證,但是貌似是這樣的。
2、當xaml發現Converter抛出異常時,會繼續執行Convert方法,最多15次。
完全不知道這個有什麼意義,已經抛出異常了,再次調用就不會有異常?
3、盡量在Converter中将異常處理好,否則容易引起不必要的麻煩。
如果條件錯誤、或者轉換失敗,則傳回null。而不是抛出異常。
寫的時間比研究的時間長多了,希望對大家有幫助,希望大神能夠回答我的疑問。
引用請注明出處:
Exception in WPF's Converter