如何获取ItemsControl中触发ContextMenu的元素并绑定命令参数
核心问题说明:ContextMenu属于独立的弹出视觉树,与主界面视觉树无直接关联,无法直接通过RelativeSource或ElementName绑定主视觉树中的元素。你当前将ContextMenu挂载在ItemsControl容器上,本身也无法直接拿到单个子项的对应数据,因此首先需要调整ContextMenu的挂载位置,再利用ContextMenu的PlacementTarget属性完成绑定。
1. 外层主题右键菜单绑定
将原本挂在ItemsControl上的主题右键菜单,移动到外层DataTemplate内的主题Button上,直接以触发菜单的Button作为绑定源。
注意:由于ContextMenu不在主视觉树中,绑定ViewModel命令时需要通过
x:Reference引用顶层窗口,或者通过PlacementTarget查找上级视觉树。我们这里采用给Window命名的方案,兼容性更强。
首先给你的顶层Window添加x:Name属性:
<Window x:Name="MainWindow" <!-- 其余属性保持不变 --> >
修改主题按钮和对应的ContextMenu代码:
<DataTemplate> <StackPanel> <!-- 主题按钮,直接把ContextMenu挂在这里 --> <Button Content="{Binding Name}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.UpdateSubthemesVisibilityCommand}" CommandParameter="{Binding Name}"> <Button.ContextMenu> <ContextMenu> <!-- 绑定到触发菜单的Button的Content,也就是主题名称 --> <MenuItem Header="Rename" Command="{Binding DataContext.RenameThemeCommand, Source={x:Reference MainWindow}}" CommandParameter="{Binding PlacementTarget.Content, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/> <MenuItem Header="Delete"/> </ContextMenu> </Button.ContextMenu> </Button> <!-- 内层子主题ItemsControl 后续代码保持不变 --> </StackPanel> </DataTemplate> <!-- 原本挂在ItemsControl上的ContextMenu可以直接删除 -->
2. 内层子主题右键菜单绑定
同理,将原本挂在内层ItemsControl上的子主题右键菜单,移动到内层DataTemplate的子主题Button上:
<ItemsControl Items="{Binding Subthemes}" IsVisible="{Binding AreSubthemesVisible}"> <ItemsControl.ItemTemplate> <DataTemplate> <Button Content="{Binding Name}"> <Button.ContextMenu> <ContextMenu> <!-- 绑定到子主题按钮的Content --> <MenuItem Header="Rename" Command="{Binding DataContext.RenameSubthemeCommand, Source={x:Reference MainWindow}}" CommandParameter="{Binding PlacementTarget.Content, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/> <MenuItem Header="Delete"/> </ContextMenu> </Button.ContextMenu> </Button> </DataTemplate> </ItemsControl.ItemTemplate> <!-- 原本挂在内层ItemsControl上的ContextMenu可以直接删除 --> </ItemsControl>
补充说明
如果你的Command参数需要接收完整的主题/子主题对象而非仅名称,也可以把参数绑定改成{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}},直接传递对应的数据实体,省去ViewModel里按名称查找的逻辑。
内容的提问来源于stack exchange,提问作者Pariah

