将上下文菜单命令参数绑定到数据网格属性

13

在我的XAML文件中,我有一个带有上下文菜单的DataGrid。数据源是一个ViewModel,它有一个名为EntityCollection(一个ObservableCollection)的属性作为DataGrid的ItemsSource,另外还有一个集合ContextMenu.MenuItems,它用作创建DataGrid上上下文菜单的数据源。该集合的元素具有绑定到命令属性的菜单项的Command属性:

<DataGrid Name="EntityDataGrid" ItemsSource="{Binding EntityCollection}" Height="450">
  <DataGrid.ContextMenu>
    <ContextMenu ItemsSource="{Binding Path=ContextMenu.MenuItems}">
      <ContextMenu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
          <Setter Property="Command" Value="{Binding Command}" />
          <Setter Property="CommandParameter"
                  Value="{Binding ElementName=EntityDataGrid, Path=SelectedItems}" />
        </Style>
      </ContextMenu.ItemContainerStyle>
    </ContextMenu>
  </DataGrid.ContextMenu>
</DataGrid>

在ViewModel中,菜单项命令的操作具有以下签名:

private void SelectedItemsAction(object parameter)
{
    // Do something with "parameter"
}

现在我的问题是,当我点击上下文菜单项时,我会进入SelectedItemsAction,但parameter为空。我相信我的问题在于CommandParameter属性的设置器中。正如你所看到的,我想通过将值设置为DataGrid的SelectedItems属性来绑定此属性:

<Setter Property="CommandParameter"
        Value="{Binding ElementName=EntityDataGrid, Path=SelectedItems}" />

我尝试了一些简单的值来进行测试:

<Setter Property="CommandParameter"
        Value="{Binding ElementName=EntityDataGrid, Path=Height}" />

这里的parameter仍然是null。现在只是为了测试是否有任何参数到达我的操作方法:

<Setter Property="CommandParameter"
        Value="10" />

这个方法已经能够正常工作了,我的action方法中的parameter现在确实是10。

我在将CommandParameter值绑定到EntityDataGrid属性时做错了什么?这是否可能?

提前感谢您的帮助!

3个回答

17

ContextMenu 不在视觉树的同一部分,所以你无法使用 ElementName 等方式引用 DataGrid。你需要使用 ContextMenuPlacementTarget。可以尝试如下方式:

<ContextMenu ItemsSource="{Binding Path=ContextMenu.MenuItems}">
    <ContextMenu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Command" Value="{Binding Command}" />
            <Setter Property="CommandParameter"
                    Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
                                    Path=PlacementTarget.SelectedItems}" />
        </Style>
    </ContextMenu.ItemContainerStyle>
</ContextMenu>

谢谢,这个也可以!(我标记了OffApps Cory的解决方案为接受的答案,只是因为他比你早几个小时回答了。无论如何,感谢你的努力!) - Slauma
3
+1 终于找到了!在使用 MVVM Light Toolkit 中的 RelayCommandCommandParameter 时,这对我很有用。我希望 WPF 能够使 ElementName 在元素树的任何位置都能工作。感谢分享这个 ContextMenu 绑定变化! - SliverNinja - MSFT
我也曾经有过使用MVVM Light的相似经历。尝试了我所知道的所有常规方法,取得了一些有限的成功,然后还尝试了许多同事的建议和谷歌搜索结果,但都没有成功。但是这个答案正是我所需要的,并且教会了我一些新东西。所以感谢@Fredrik Hedblad 8年后的帮助。 - Yonabart

9
你尝试过执行祖先绑定吗?类似这样的操作: ```html ```
<Setter Property="CommandParameter"
        Value="{Binding Path=SelectedItems, 
        RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />

太好了,谢谢你,这个可行!(现在我只需要再多读一些关于WPF绑定的内容,才能完全理解你的代码在做什么;)) - Slauma
它正在沿着树向上查找与该类型匹配的项。它遇到的第一个项将被用作绑定上下文。 - CodeWarrior
与此同时,我已经更多地了解了绑定,现在清楚了为什么我的原始标记无法工作以及为什么您的代码是正确的方式。感谢您的解释! - Slauma
2
无法更改此内容,因为它只是一个一字符的编辑,但是在 {x:Type DataGrid}} 之后您需要多加一个闭合括号。 - Sterno

0
<Setter Property="CommandParameter"
    Value="{Binding Path=SelectedItems, 
    RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />

如果您使用CodeWarrior答案中的代码,您可以在ViewModel端使用下面的代码将其转换为任何类型。

System.Collections.IList items = (System.Collections.IList)obj;
var collection = items.Cast<Foo>();

之后

foreach(var item in collection)
{
.
.

但如果你只需要一行代码,下面的代码就足够了。

 CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"

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