天天看點

[WPF 容易忽視的細節] —— Exception in WPF's Converter前言:問題:

原文: [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