18

In WPF, when I right-click on a treeview item I would like it to be Selected/Activated before showing the context menu.

This sounds pretty simple, but the inclusion of a hierachicalDataTemplate complicates things a little.

I have the following treeview:

<TreeView 
            x:Name="trv"
            ContextMenu="{StaticResource contextMenu}"
            ItemTemplate="{StaticResource treeHierarchicalDataTemplate}"
            ItemsSource="{Binding Source={StaticResource meetingItems}}" >

            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="trv_PreviewMouseRightButtonDown"/>
                    <Setter Property="IsExpanded" Value="True"></Setter>
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>

And here is my event handler...

private void trv_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem item = sender as TreeViewItem;
    if (item != null)
    {
        item.Focus();
        e.Handled = true;
    }

}

Note how I add an EventSetter above. This ALMOST works. But it only selects the root-level treeview node (i.e. the root parent of the node on which I right click). This may be because of my hierarchical data template? This template can contain children OF THE SAME TYPE.

Here is my hierarchical data template...

<HierarchicalDataTemplate x:Key="treeHierarchicalDataTemplate" 
                          ItemsSource="{Binding Path=ChildMeetingItems}">
    <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=Red}" Value="True">
            <Setter TargetName="img" Property="Image.Source" Value="pack://siteoforigin:,,,/images/bookRed.png"></Setter>
        </DataTrigger>
    </HierarchicalDataTemplate.Triggers>
    <StackPanel 
        x:Name="treeViewItemPanel"
        Background="Transparent"
        Orientation="Horizontal">
        <Image Width="16" Height="16"  x:Name="img" Margin="0,0,4,0" Source="pack://siteoforigin:,,,/images/bookGreen.png"></Image>
        <TextBlock Foreground="DarkGray" Text="{Binding DisplayIndex}" Margin="0,0,5,0"></TextBlock>
        <TextBlock Text="{Binding Summary}"></TextBlock>
    </StackPanel>
</HierarchicalDataTemplate>

Any idea on why only the root node instead of child nodes are selected when I right-click?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
willem
  • 25,977
  • 22
  • 75
  • 115

3 Answers3

17

That's because the ItemContainerStyle is not inherited by the child nodes. You need to add the same EventSetter on the ItemContainerStyle o your HierarchicalDataTemplate.

<HierarchicalDataTemplate x:Key="treeHierarchicalDataTemplate" 
                          ItemsSource="{Binding Path=ChildMeetingItems}">
    <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=Red}" Value="True">
            <Setter TargetName="img" Property="Image.Source" Value="pack://siteoforigin:,,,/images/bookRed.png"></Setter>
        </DataTrigger>
    </HierarchicalDataTemplate.Triggers>
    <StackPanel 
        x:Name="treeViewItemPanel"
        Background="Transparent"
        Orientation="Horizontal">
        <Image Width="16" Height="16"  x:Name="img" Margin="0,0,4,0" Source="pack://siteoforigin:,,,/images/bookGreen.png"></Image>
        <TextBlock Foreground="DarkGray" Text="{Binding DisplayIndex}" Margin="0,0,5,0"></TextBlock>
        <TextBlock Text="{Binding Summary}"></TextBlock>
    </StackPanel>

<HierarchicalDataTemplate.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="trv_PreviewMouseRightButtonDown"/>                    
                </Style>
            </HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
Denis Troller
  • 7,411
  • 1
  • 23
  • 36
  • Thanks Denis. That makes sense. I'm still getting strange behaviour though... if I set the ItemContainerStyle on both the hierarchicalDataTemplate and the treeview, then it still does the same as before. If I only set the ItemContainerStyle on the hierarchicalDataTemplate, it does the same thing, but this time for nodes on the Second level only. Strange. Any ideas? – willem Apr 28 '09 at 14:10
  • 1
    Aah, interesting. That was happening due to the e.Handled = true. Not 100% sure why though... – willem Apr 28 '09 at 14:36
  • 4
    Because your are handling a tunneling event (a PreviewXXX event). These events go from the top to the bottom of the tree, so if you set e.Handled= true at the root, it will stop there and not tunnel down the tree to your item. – Denis Troller Apr 28 '09 at 15:21
  • 2
    I found it more natural to use MouseRightButtonDown (rather than the preview version). That meant you _should_ use the `e.Handled = true` but it was easier to integrate having _Ctrl_-right-click logic to deal with multiple-selection. (Ctrl-right click was selecting all the nodes in the hierarchy because of the tunnelling with the preview event and no `e.Handled`.) – quarkonium Aug 08 '13 at 02:57
6

just comment the e.Handler=true from your event handler.

like this:

private void trv_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem item = sender as TreeViewItem;
    if (item != null)
    {
        item.Focus();
       // e.Handled = true;
    }

}
stema
  • 90,351
  • 20
  • 107
  • 135
Shafi
  • 61
  • 1
  • 1
  • This works for my multi-level treeview. ```e.Handled = true;``` will stop handler event and stops it at first level, that's why only root element or first level element can be selected. – Adison Jun 28 '17 at 07:19
0

I had the same problem - couldn't get the proper selected tree item. And instead of using PreviewMouseRightButtonDown event I used same event of a StackPanel which also stores all needful data:

<StackPanel DataContext="{Binding}" MouseLeftButtonDown="StackPanel_MouseLeftButtonDown">
....
</StackPanel>

And the event handler code-behind:

 private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            StackPanel panel = sender as StackPanel;
            if(panel==null)return;
            MyTreeViewItem myClicked = panel.DataContext as MyTreeViewItem;
            if (myClicked == null) return;
...
}

MyTreeViewItem is my custom type for a data; myClicked now stores a data associated with the clicked tree item. Hope it will help someone like me.

lena
  • 1,181
  • 12
  • 36