【wpf】数据模板 + ContextMenu 导致的命令 绑定失效

背景

测试环境  .net6 wpf

TreeView  中我写了一个数据模板,数据模板的StackPanel 中,我加了一个ContextMenu右键菜单。

<TreeView ItemsSource="{Binding Path=WorkflowViewToolsTree}">
    <TreeView.ItemTemplate>
        <!--子项的绑定-->
        <HierarchicalDataTemplate DataType="{x:Type local_md:ToolsNodeItem}" ItemsSource="{Binding Path=Children}">
            <StackPanel Orientation="Horizontal" Background="Transparent">
                <StackPanel.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="删除" Command="{Binding MenuItemCmd, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}" CommandParameter="123"/>
                        <MenuItem Header="上移" Command="{Binding MenuItemCmd}" CommandParameter="123"/>
                        <MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl,AncestorLevel=4}}"  CommandParameter="123"/>
                        <!--<MenuItem Header="{Binding Path=Name, Source={x:Reference Name}}"/>-->
                    </ContextMenu>
                </StackPanel.ContextMenu>
                <Image Source="{Binding Icon}" Width="20" Margin="0,0,10,0"/>
                <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
                <StackPanel.ToolTip>
                    <TextBlock Text="{Binding TypeName}"/>
                </StackPanel.ToolTip>
            </StackPanel>
            
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

结果 ContextMenu一切绑定都无效了,RelativeSource各种找都没有用!

一开始以为是模板导致的,但模板中其他元素的绑定都是OK的,说明应该不是模板的问题。于是我开始怀疑是ContextMenu的问题。

有的文章说:ContextMenu是一个独立的窗口不在可视化树中,所以向上找不到。

于是我在一个其他元素中写了ContextMenu,发现一切正常:

<Button>
    <Button.ContextMenu>
        <ContextMenu>
            <MenuItem Header="删除" Command="{Binding MenuItemCmd}" CommandParameter="123"/>            
        </ContextMenu>
    </Button.ContextMenu>
</Button>

解决方案

使用x:Reference

<MenuItem Header="删除" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}"  CommandParameter="删除"/>
<MenuItem Header="上移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}"  CommandParameter="上移"/>
<MenuItem Header="下移" Command="{Binding Path=DataContext.MenuItemCmd, Source={x:Reference Name=top_uc}}"  CommandParameter="下移"/>

这里有个解决方法是通过 x:Reference 指定绑定对象的Name(代码中的 top_uc 是我给被绑定控件取的名字),然后明确的指定Path。如果绑定的内容在DataContext里面,这个DataContext是不能丢的,一定要写上!

小结

目前的判断是 Command 绑定的特殊性,如果Command 处于数据模板之内,就必须写全。

<MenuItem Header="下移2" Command="{Binding Path=DataContext.MenuItemCmd, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>

如果Command即在模板里,还在ContextMenu里。那就必须使用x:Reference 了

结束

目前只是看到现象,如果哪位大佬知道具体原因,请和我在评论区探讨,感谢!

最后,强调一下,我是在 .net6 wpf 下做的测试,其他环境可能表现不一样。

posted @ 2022-12-01 15:11  宋桓公  阅读(368)  评论(0编辑  收藏  举报