WPF OpenFileDialog 抑制异常

发布于 2024-10-24 08:40:37 字数 6192 浏览 5 评论 0原文

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

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

以下是重现此行为的示例。如果运行该示例,您将看到后面的代码中引发的异常以及应用程序中的 UnHandledException 处理程序正确捕获了 ShellModel.ThrowException 属性。 xaml.cs。但是,使用 OpenFileDialog 后在 ShellModel.OpenFile 属性中引发的异常已被抑制。

为什么这些例外会被抑制?

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
}

现在可以捕获绑定错误!

Perhaps I'm using the OpenFileDialog incorrectly, but I've found that unhandled exceptions are being suppressed whenever the OpenFileDialog is used and the result is passed into my model.

Typically I'll hook ino the AppDomain.CurrentDomain.UnhandledException event to handle any unhandled exceptions, but any exceptions being raised after using the OpenFileDialog are swallowed whole.

The following is an example to reproduce this behavior. If you run the example, you'll see that the exceptions being thrown in the code behind and the ShellModel.ThrowException property are correctly caught by the UnHandledException handler in the App.xaml.cs. However, the exception being thrown in the ShellModel.OpenFile property after using the OpenFileDialog is being suppressed.

Why would these exceptions be suppressed?

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 ) );
            }
        }
    }
}

Resolution

As noted in the answer, this isn't an OpenFileDialog issues, rather its a data binding issue.

Bradley's answer and Hans possible duplicate link pointed to some great information. The links/articles didn't quite provide the resolution I came up with, re: I found there was another exception I could hook into: AppDomain.CurrentDomain.FirstChanceException

Here's a modified version of my 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
}

The binding errors are now caught!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

带刺的爱情 2024-10-31 08:40:37

抑制异常的不是 OpenFileDialog,而是 WPF 数据绑定。默认情况下,属性绑定中涉及的 C# 代码抛出的任何异常都将被数据绑定引擎吞掉。 (您可以在代码中演示这一点,只需将 OnFindFile 的内容替换为 OpenFile.Content = "test";)。

要诊断数据绑定错误,请将侦听器添加到 PresentationTraceSources.DataBindingSource 跟踪源。 Bea Costa 有一篇很好的博客文章描述了如何做到这一点。

It's not the OpenFileDialog that's suppressing the exception, but WPF data binding. By default, any exception thrown from C# code that's involved in a property binding will be swallowed by the data binding engine. (You can demonstrate this in your code by replacing the contents of OnFindFile with just OpenFile.Content = "test";).

To diagnose data binding errors, add a listener to the PresentationTraceSources.DataBindingSource trace source. Bea Costa has a good blog post describing how to do this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文