Search code examples
c#wpfexceptionopenfiledialogunhandled-exception

WPF OpenFileDialog Suppresses Exception


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!


Solution

  • 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.