Search code examples
c#wpftreeview

ContextMenuOpening event fired for both TreeView and TreeViewItem


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.


Solution

  • You can try using the ContextMenuEventArgs.Handled value. https://learn.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);
    }