Search code examples
c#wpfxamldata-bindingtreeviewitem

WPF: TreeViewItem Expanded/Collapsed Event Catches Exceptions


I'm working on a WPF application in C#, and I've come across an interesting problem. Why are exceptions which are thrown from an Expanded/Collapsed event in a TreeViewItem ignored? Other events, including those in a TreeViewItem like MouseDoubleClick, exhibit normal behavior for an exception. The Diagnostic Tools in Visual Studio shows that the exception is being thrown and then caught in the PresentationFramework code. As an example,

XAML

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TreeView>
            <TreeViewItem Header="Parent" Expanded="TreeViewItem_Expanded" MouseDoubleClick="TreeViewItem_MouseDoubleClick">
                <TreeViewItem Header="Child" />
            </TreeViewItem>
        </TreeView>
    </Grid>
</Window>

XAML.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
    {
        throw new Exception("Ignored"); //Same issue with Collapsed event
    }

    private void TreeViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        throw new Exception("Normal exception behavior");
    }
}

I'm working on implementing a lazy-loaded TreeView, and I have spent the better part of a day debugging my application only to find out that it was silently failing and the original problem was a simple fix. Is my approach of using the Expanded and Collapsed events completely wrong? If not, is there any graceful way to not have these exceptions silently fail?


Solution

  • This behavior is not specific to the Expanded event or any other events.

    The reason is that your exceptions get "swallowed" by the WPF Binding Engine. The default ControlTemplate of the TreeViewItem contains a ToggleButton that binds its IsChecked property to the TreeViewItem.IsExpanded property using the two-way mode:

    <ToggleButton IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/>
    

    So the whole process looks like:

    1. You click on the item's expand icon (actually, this is the ToggleButton).
    2. The ToggleButton sets its IsChecked property to true.
    3. The WPF Binding Engine transfers the new value (true) to the TreeViewItem's IsExpanded property.
    4. The tree item expands and raises the Expanded event.
    5. You throw the exception in the event handler.
    6. Since we are still processing the Binding transfer (step 3), the Binding Engine catches the exception and logs it to a configured TraceListener.
    7. The workflow continues, you don't see the exception on the caller's stack.

    The WPF Binding Engine catches the exceptions natively, you cannot change it. But you could implement your own TraceListener that can throw the provided exceptions or maybe do some other error handling.

    Take a look on this question and its answers.