In WPF, how do I select the treeview item under my cursor on right-click?
Asked Answered
M

3

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?

Mammal answered 28/4, 2009 at 13:35 Comment(0)
R
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>
Reactance answered 28/4, 2009 at 13:39 Comment(4)
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?Mammal
Aah, interesting. That was happening due to the e.Handled = true. Not 100% sure why though...Mammal
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.Reactance
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.)Qualification
D
7

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;
    }

}
Disrepair answered 15/11, 2011 at 7:21 Comment(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.Bisitun
P
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.

Pissed answered 16/9, 2013 at 8:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.