WPF OpenFileDialog 抑制异常
也许我错误地使用了 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
抑制异常的不是 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 justOpenFile.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.