Search code examples
wpfxamltreeview

HowTo implement commands for selection of treenodes in WPF treeview


Again I'm a bit lost in WPF treeview.
I populated my treeview with some data and I'd like to fire a command, when clicking on a node and get its values in that command.

My treeview-xaml looks like this:

        <TreeView DataContext="{Binding ProjectTree}" ItemsSource="{Binding ProjectNode}" DockPanel.Dock="Left" Margin="0 0 2 0" x:Name="ProjectTree" Grid.Column="0">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
                <Setter Property="FontWeight" Value="Normal"/>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="FontWeight" Value="Bold"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>

        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <Image Margin="3" Source="{Binding ItemType, Converter={x:Static misc:TreeItemImageConverter.Instance }}" Width="20" />
                    <TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

How can I trigger a command on click on a treeviewitem here?

SOLUTION

After some experiments, It works for me that way:

xaml:

    <TreeView DataContext="{Binding ProjectTree}" ItemsSource="{Binding ProjectNode}" DockPanel.Dock="Left" 
              x:Name="ProjectTree" Margin="0 0 2 0" Grid.Column="0">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <i:InvokeCommandAction Command="{Binding TreeNodeSelected}" 
                                       CommandParameter="{Binding ElementName=ProjectTree, Path=SelectedItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
                <Setter Property="FontWeight" Value="Normal"/>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="FontWeight" Value="Bold"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TreeView.ItemContainerStyle>

        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <Image Margin="3" Source="{Binding ItemType, Converter={x:Static misc:TreeItemImageConverter.Instance }}" Width="20" />
                    <TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

In the Viewmodel of the treeview:
C#

    public RelayCommand TreeNodeSelected { get; private set; }

    readonly ReadOnlyCollection<PlcAddressViewModel> _rootNodes;
    readonly PlcAddressViewModel _rootAddress;

    #region Constructor
    public ProjectTreeviewModel(PlcAddress rootAddress)
    {
        _rootAddress = new PlcAddressViewModel(rootAddress);

        _rootNodes = new ReadOnlyCollection<PlcAddressViewModel>(
            new PlcAddressViewModel[]
            {
                _rootAddress
            });

        TreeNodeSelected = new RelayCommand(ExecuteTreeNodeSelected, canExecuteMethod);
    }
    #endregion Constructor

    #region Commands
    private bool canExecuteMethod(object parameter)
    {
        return true;
    }
    private void ExecuteTreeNodeSelected(object parameter)
    {
        PlcAddressViewModel selectedNode = (PlcAddressViewModel)parameter;
                Console.WriteLine("Found this node: " + selectedNode.Name);

        return;
    }
    #endregion Commands

Thanks to @mm8 and @BionicCode


Solution

  • You could handle an event like for example SelectedItemChanged using an interaction trigger:

    <TreeView DataContext="{Binding ProjectTree}"
                ItemsSource="{Binding ProjectNode}" 
                x:Name="ProjectTree"
                xmlns:i="http://schemas.microsoft.com/xaml/behaviors">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged" >
                <i:InvokeCommandAction Command="{Binding MouseEnterCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <TreeView.ItemContainerStyle>
            ...
        </TreeView.ItemContainerStyle>
        ...
    </TreeView>
    

    Handling events in an MVVM WPF application

    How to add System.Windows.Interactivity to project?

    But why don't you just bind the SelectedItem property to a source property and handle your logic in the setter of this one? This would be MVVM way of doing this.

    Edit: Since the SelectedItem property a TreeView is read-only, you'll have to use a behaviour to be able to bind to it. There is an example of how to do this here.