在ItemsControl.DataTemplate中为ContextMenu设置DataContext

5
我有一个自定义控件,它被用于ItemsControl的数据模板中。我想在每个项目上放置一个ContextMenu,并调用UserControl的视图模型来处理命令。使用下面的XAML,我可以让自定义控件上的单击事件调用用户控件视图模型中的SelectedItemCommand。但是,使用类似语法的上下文菜单会失败。默认情况下,每个自定义控件都会得到视图模式。我使用的任何RelativeSource语法值都无法解析为用户控件的视图模型(RelativeSource Self)。
魔术代码是什么?
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <controls:MyCustomItem Width="Auto"
                                                 Command="{Binding DataContext.SelectedItemCommand,
                                                                   RelativeSource={RelativeSource FindAncestor,
                                                                                                  AncestorType={x:Type ItemsControl}}}"
                                                 CommandParameter="{Binding}">
                    <controls:MyCustomItem.ContextMenu>
                        <ContextMenu>
                            <MenuItem Command="{Binding DataContext.ClearAlarmsCommand,
                                                        RelativeSource={RelativeSource FindAncestor,
                                                                                       AncestorType={x:Type ItemsControl}}}"
                                      Header="Clear All" />
                        </ContextMenu>
                    </controls:MyCustomItem.ContextMenu>
                </controls:MyCustomItem>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

你不能在ContextMenu中使用RelativeSource绑定,因为它使用的Popup位于可视树之外。你可以尝试使用ElementName绑定,这可能会起作用,但我总是记不住... - McGarnagle
另一个选择是使用Freezable作为绑定代理。将Freezable绑定到UserControl.Resources中的ItemsControl的DataContext,然后您可以在ContextMenu绑定中绑定到Freezable。如果没有更好的解决方案,我可以在午餐后发布一个示例作为答案。 - Lee O.
1个回答

12

ContextMenu不在与ItemsControl相同的可视树中,因此在绑定时RelativeSourceElementName无法工作,因为它们需要遍历可视树来查找源。

如果您使用的是WPF 4.0或更高版本,则可以使用x:Reference标记扩展与ItemsControl数据上下文进行绑定。

ItemsControl上设置x:Name并使用x:Reference进行绑定,如下所示:

<ItemsControl x:Name="itemsControl">
   ....
   <MenuItem Command="{Binding DataContext.ClearAlarmsCommand,
                               Source={x:Reference itemsControl}}"
             Header="Clear All" />
   ....
</ItemControl>

如果你的目标是低于 WPF 4.0 的版本,你也可以使用 Freezable BindingProxy 方法。请参考我的答案 这里 中的方法。


1
谢谢您,大师。它起作用了!我已经寻找解决方案有一段时间了。现在我在WPF中学到了新的东西!谢谢。 - Ladislav Ondris
1
老兄,你救了我一命!我已经尝试了2个小时所有其他方法都没有成功,我快要放弃了,但是这个方法太美妙了!谢谢! - drkthng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接