1

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

Telefisch
  • 305
  • 1
  • 19
  • I have removed my answer as overriding the `TreeViewItem` is too complicated compared to other solutions. `TreeViewItem` is quite complex (consisting of `ToggleButton`, `ItemsControl` and a lot of logic etc.). See [the original template](https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/treeview-styles-and-templates#treeview-controltemplate-example). Doing this with a `ListBoxItem` is quite simple as it consists only of a `ContentPresenter` which you would have to replace with a `Button`. If you are still interested let me know. It takes a minute to post the correct template. – BionicCode May 28 '20 at 13:22
  • Hi BionicCode, thank you very much for your approach. I will first try to do it the way mm8 mentioned because it seems to be a bit easier for a beginner like me. WPF could be really confusing... – Telefisch May 28 '20 at 13:40

1 Answers1

2

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.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I like your approach, as I have a IsSelected-property already but I don't know how to inform the rest of the app, that this property changed. Isn't it encapsulated inside the TreeviewItem? I mean, I don't have any event fired. (updated my question) – Telefisch May 28 '20 at 13:38
  • Please ask a new question if you have another issue or changed your approach. – mm8 May 28 '20 at 13:41