WPF OpenFileDialog抑制异常

时间:2011-03-19 18:46:06

标签: c# wpf exception-handling openfiledialog unhandled-exception

也许我错误地使用OpenFileDialog,但我发现只要使用OpenFileDialog并且结果传递到我的模型中,就会抑制未处理的异常。

通常我会挂钩AppDomain.CurrentDomain.UnhandledException事件来处理任何未处理的异常,但是使用OpenFileDialog后引发的任何异常都会被吞没。

以下是重现此行为的示例。如果运行该示例,您将看到后面代码中抛出的异常和ShellModel.ThrowException属性被App.xaml.cs中的UnHandledException处理程序正确捕获。但是,使用ShellModel.OpenFile后在OpenFileDialog属性中抛出的异常将被禁止。

为什么要抑制这些例外?

App.xaml.cs

using System;
using System.Text;
using System.Windows;

namespace ExceptionTest
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup( StartupEventArgs e )
        {
            base.OnStartup( e );

            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        }

        private void OnUnhandledException( object sender, UnhandledExceptionEventArgs e )
        {
            var ex = e.ExceptionObject as Exception;

            if( ex == null )
            {
                MessageBox.Show( string.Format( "Null Exception: {0}", e ) );
                return;
            }

            var sb = new StringBuilder();
            sb.AppendLine( "An unhandled exception was encountered. Terminating now." );
            sb.AppendLine();
            sb.AppendLine( "Exception:" );
            sb.AppendLine( ex.Message );

            MessageBox.Show( sb.ToString(), "Whoops...", MessageBoxButton.OK, MessageBoxImage.Error );

            Environment.Exit( 1 );
        }
    }
}

Shell.xaml

<Window x:Class="ExceptionTest.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Model="clr-namespace:ExceptionTest"
        Title="Exception Test" Height="350" Width="350" WindowStartupLocation="CenterScreen">

    <Window.DataContext>
        <Model:ShellModel x:Name="Model" />
    </Window.DataContext>

    <StackPanel Orientation="Vertical" VerticalAlignment="Stretch">

        <Button 
            Click="OnCodeBehind" Margin="20"
            Content="Exception from code behind" Height="25" Width="250" />

        <Button 
           Click="OnThrowExeption"  Margin="20"
            Content="Exception from Model" Height="25" Width="250" />

        <Button 
            Click="OnFindFile" Margin="20"
            Content="Exception from OpenFileDialog" Height="25" Width="250" />

        <Label Content="{Binding OpenFile, Mode=TwoWay}" x:Name="OpenFile"
                     Height="28" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="Auto" />

    </StackPanel>
</Window>

Shell.xaml.cs / Model

using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Windows;
using Microsoft.Win32;  

namespace ExceptionTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class Shell : Window
    {
        private OpenFileDialog OpenDialog { get; set; }

        public Shell()
        {
            InitializeComponent();

            OpenDialog = new OpenFileDialog();
            string path = new Uri( Assembly.GetExecutingAssembly().CodeBase ).LocalPath;
            OpenDialog.InitialDirectory = Path.GetDirectoryName( path );
            OpenDialog.Multiselect = false;
            OpenDialog.Title = "Find File";
            OpenDialog.RestoreDirectory = true;
        }

        private void OnCodeBehind( object sender, RoutedEventArgs e )
        {
            throw new Exception( "Exception from Code Behind." );
        }

        private void OnThrowExeption( object sender, RoutedEventArgs e )
        {
            Model.ThrowException = "Test";
            e.Handled = true;
        }

        private void OnFindFile( object sender, RoutedEventArgs e )
        {
            OpenDialog.ShowDialog( this );

            string fileName = OpenDialog.FileName;

            if( !string.IsNullOrEmpty( fileName ) )
            {
                OpenDialog.InitialDirectory = Path.GetDirectoryName( fileName );
                OpenFile.Content = fileName;
            }
        }
    }

    public class ShellModel : INotifyPropertyChanged
    {
        private string _throwException;
        public string ThrowException
        {
            get { return _throwException; }
            set
            {
                _throwException = value;
                NotifyPropertyChanged( "ThrowException" );
                throw new Exception( "Exception from Model." );
            }
        }

        private string _openFile;
        public string OpenFile
        {
            get { return _openFile; }
            set
            {
                _openFile = value;
                NotifyPropertyChanged( "OpenFile" );
                throw new Exception( "Exception from Model after using OpenFileDialog." );
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged( String info )
        {
            if( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( info ) );
            }
        }
    }
}

分辨率

如答案所述,这不是OpenFileDialog问题,而是数据绑定问题。

布拉德利的答案和汉斯可能重复的链接指出了一些很好的信息。链接/文章并没有完全提供我提出的解决方案,重新:我发现还有另外一个例外我可以加入:AppDomain.CurrentDomain.FirstChanceException

以下是我App.Xaml.cs的修改版本:

protected override void OnStartup( StartupEventArgs e )
{
    base.OnStartup( e );

    AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;

    // The FirstChanceException will catch binding errors
    AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
}


private void OnFirstChanceException( object sender, FirstChanceExceptionEventArgs e )
{
  // do stuff
}

现在抓住了绑定错误!

1 个答案:

答案 0 :(得分:1)

OpenFileDialog不是抑制异常,而是WPF数据绑定。默认情况下,数据绑定引擎会吞下从属性绑定中涉及的C#代码抛出的任何异常。 (您可以在代码中通过仅使用OnFindFile替换OpenFile.Content = "test";的内容来证明这一点。)

要诊断数据绑定错误,请向PresentationTraceSources.DataBindingSource跟踪源添加侦听器。 Bea Costa a good blog post描述了如何做到这一点。