Search code examples
c#.netwpfmvvmtreeview

TreeView - RelayCommand Does Not Execute Despite Breakpoint Hitting


I have a TreeView with an ItemTemplate/HierarchicalDataTemplate. When I expand a TreeViewItem, I want to trigger a MessageBox. The issue I am currently experiencing is that despite the breakpoint being hit on the RelayCommand() in the ViewModel, the actual MessageBox does not execute.

I tried moving the command inside the KeyViewModel itself, and the same behavior occurred.

Reproducible Project: https://github.com/MemeMachineSO/TreeViewProblem

enter image description here

XAML:

<Window x:Class="TreeViewProblem.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:TreeViewProblem"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TreeView Grid.Row="1" Grid.Column="0" Name="RegistryTreeView" ItemsSource="{Binding Keys}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding SubKeys}">
                    <TreeViewItem Header="{Binding name}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="Expanded">
                                <i:InvokeCommandAction Command="{Binding ElementName=RegistryTreeView, Path=DataContext.TreeViewItemExpanded}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </TreeViewItem>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>


Solution

  • First of all you have set the breakpoint in the getter of the property. The MessageBox.Show method is called when the command executes. It's not called when the InvokeCommandAction binds to the property itself.

    Second, the HierarchicalDataTemplate is not supposed to contain a TreeViewItem. The control creates its own TreeViewItem containers and the Expanded event of the one in your template is never fired.

    Using an EventTrigger won't work here unless you define a (complete) custom template for the TreeView.

    Instead you could define an ItemContainerStyle and invoke the command programmatically:

    public partial class MainWindow : Window
    {
        private readonly MainWindowViewModel _viewModel = new MainWindowViewModel();
        public MainWindow()
        {
            InitializeComponent();
            DataContext = _viewModel;
        }
    
        private void TreeViewItem_Expanded(object sender, RoutedEventArgs e) =>
            _viewModel.TreeViewItemExpanded.Execute(null);
    }
    

    XAML:

    <TreeView Grid.Row="1" Grid.Column="0" Name="RegistryTreeView" ItemsSource="{Binding Keys}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <EventSetter Event="Expanded" Handler="TreeViewItem_Expanded" />
            </Style>
        </TreeView.ItemContainerStyle>
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding SubKeys}">
                <TextBlock Text="{Binding name}" />
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    

    And no, this does not break the MVVM pattern as you invoke the very same command from the very same view. MVVM is not about eliminating code. It's about separation of concerns. Whether you invoke the view model command from the code-behind of the view or using an EventTrigger and an InvokeCommandAction from the XAML markup of the very same view doesn't matter as far as the pattern is concerned.