WPF 开发中右键菜单无法绑定 Command 的问题
问题描述
这是在使用 WPF 开发时,右键菜单的 Command 绑定可能会遇到的一个问题:
在使用 DataGrid 对 DataGridRow 进行右键菜单绑定 Command 时,我想要绑定到 ViewModel 中的 ICommand 对象,但是有以下问题导致无法绑定成功。
- DataGridRow 的 DataContext 是 DataGrid 的绑定的 Items 中的一个,所以没有办法直接拿到 ViewModel,无法绑定到 ICommand 对象
- ContextMenu 是 Popup 的形式显示的,所以它不在可视化树上,无法通过 FindAncestor 等方法找到 DataGrid 或 UserControl 的 DataContext。
解决方案
通过调研发现了有三种解决方案:
- 给最外层的控件(通常是 UserControl/Window)或 DataGrid 设个 Name,在绑定时使用 ElementName 绑定到 ViewModel。
- 借助 PlacementTarget + Tag 中转 ViewModel,将 DataContext 传递给 DataGridRow 的 Tag 中,ContextMenu 中再通过 PlacementTarget.Tag 获取。
- 使用 Freezable BindingProxy,提供一个全局的 DataContext 访问方式。
然而在实现的过程中发现,方案 1\2 仍然无法获取 DataContext。可能和 ContextMenu 的 Popup 特性有关,导致无法通过常规方式获取 DataContext。
所以只整理了方案 3 的实现。
使用 Freezable BindingProxy
定义一个继承了 Freezable 的 BindingProxy:
// BindingProxy.cs
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore() => new BindingProxy();
public object Data
{
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(nameof(Data), typeof(object), typeof(BindingProxy));
}在顶层 View 的 Resources 中挂 proxy:
<UserControl.Resources>
<local:BindingProxy x:Key="VmProxy" Data="{Binding}" />
</UserControl.Resources>RowStyle 里直接从 Proxy 拿命令:
<ContextMenu>
<MenuItem
...
Command="{Binding Data.ImageOpenCommand, Source={StaticResource VmProxy}}" />
...
</ContextMenu>