ContextMenuOpening event fired for both TreeView and TreeViewItem

Viewed 141

My application uses TreeView populated with custom nodes defined in TreeView.ItemTemplate. Content of each node is wrapped into StackPanel with Node_ContexMenuOpening event that populates context menu based on some application properties, which is working.

XAML:

<TreeView x:Name="treeNodes" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="TreeNodes_ContextMenuOpening">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type c:MyCustomType}" ItemsSource="{Binding MyCustomTypeChildren}">
                    <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="Node_ContextMenuOpening" >
                        <Image Source="Frontend\Images\import.png" MaxWidth="15" MaxHeight="15"/>
                        <TextBlock Width="5"/>
                        <TextBlock Text="{Binding CustomTypeName}" MinWidth="100"/>
                        <TextBlock Width="10"/>
                        <Image Source="CustomImagePath" MaxWidth="15" MaxHeight="15"/>
                        <TextBlock Width="5"/>
                        <TextBlock Text="{Binding CustomTypeName2}"/>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

Code behind:

private void Node_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            FrameworkElement fe = sender as FrameworkElement;
            // get context menu and clear all items (empty menu with single placeholder item 
            // is assigned in XAML to prevent "no object instance" exception)
            ContextMenu menu = fe.ContextMenu;
            menu.Items.Clear();
            // populate menu there
        }

I would like to have same functionality on TreeView (treeview specific context menu when right clicking on empty area of treeview), which also works.

private void TreeNodes_ContextMenuOpening(object sender, ContextMenuEventArgs e)
    {
        TreeView tw = sender as TreeView;
        ContextMenu menu = tw.ContextMenu;
        menu.Items.Clear();
        // poopulate menu there
    }

But the issue is that TreeNodes_ContextMenuOpening is fired even after right clicking at TreeView node, right after Node_ContextMenuOpening is handled, which overwrites context menu for clicked node. I tried to solve it using:

 // also tried IsMouseOver and IsMouseCaptureWithin
 if (tw.IsMouseDirectlyOver)
        {
            // handle TreeNodes_ContextMenuOpening event there
        }

but without success. Any suggestions? Thanks in advance.

1 Answers

You can try using the ContextMenuEventArgs.Handled value. https://docs.microsoft.com/en-us/dotnet/api/system.windows.routedeventargs.handled?view=netcore-3.1#System_Windows_RoutedEventArgs_Handled

Gets or sets a value that indicates the present state of the event handling for a routed event as it travels the route.

Example

protected override void OnPreviewMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
    e.Handled = true; //suppress the click event and other leftmousebuttondown responders
    MyEditContainer ec = (MyEditContainer)e.Source;
    if (ec.EditState)
    { ec.EditState = false; }
    else
    { ec.EditState = true; }
    base.OnPreviewMouseRightButtonDown(e);
}
Related