将CommandParameter从MenuItem绑定到Parent DataGrid

时间:2017-01-02 18:55:57

标签: c# wpf xaml mvvm datagrid

如何将命令参数从MenuItem绑定到父Grid DataContext?

我有一个带有ContextMenu的DataGrid,将菜单项绑定到ViewModel命令,但命令参数始终为null。

我在DataGrid中使用Tag参数来访​​问DataContext并使用所需的命令,但可以想出来从每一行获取绑定数据以用作命令参数。

我已经在这里找到了很多答案,但是找不到任何有效的人,调用ViewModel中的命令参数并且命令参数始终为null。

C#

public class People
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class PeopleWindowViewModel
{
    public List<People> Peoples { get; set; }

    public PeopleWindowViewModel()
    {
        // populate Peoples list...
    }

    public ICommand RemoveCommand
    {
        get
        {
            return RelayCommand.Create((m) =>
            {
                // m always null
            });
        }
    }
}

public class PeoplePage : Page
{
    public PeoplePage()
    {
        InitializeComponent();

        DataContext = new PeopleWindowViewModel();
    }
}

XAML:                            

  <DataGrid
      Margin="0 8 0 8"
      d:DataContext="{d:DesignInstance local:People}"
      IsReadOnly="True"
      ItemsSource="{Binding Peoples}"
      Tag="{Binding DataContext,
                      RelativeSource={RelativeSource AncestorType={x:Type Page}}}">
    <DataGrid.Columns>
      <DataGridTextColumn
          Binding="{Binding Id}"
          Header="Id" />
      <DataGridTextColumn
          Binding="{Binding Name}"
          Header="Name" />
    </DataGrid.Columns>

    <DataGrid.ContextMenu>
      <ContextMenu
          Tag="{Binding Path=PlacementTarget.Tag,
                              RelativeSource={RelativeSource Self}}">
        <MenuItem
            Command="{Binding PlacementTarget.Tag.RemoveCommand,
                                      RelativeSource={RelativeSource Mode=FindAncestor,
                                                                     AncestorType=ContextMenu}}"
            CommandParameter="{Binding Path=Id,
                                               RelativeSource={RelativeSource Mode=FindAncestor,
                                                                              AncestorType=DataGrid}}"
            Header="Remover" />
      </ContextMenu>
    </DataGrid.ContextMenu>
  </DataGrid>
</Page>

2 个答案:

答案 0 :(得分:3)

我找到了解决方案,但我不确定是否有更好的解决方案。无论如何,你可以这样做:

<DataGrid ItemsSource="{Binding Peoples}">
    <DataGrid.Resources>
        <ContextMenu x:Key="ctx_menu">
            <ContextMenu.Resources>
                <Style TargetType="{x:Type MenuItem}">
                    <Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                </Style>
            </ContextMenu.Resources>
            <MenuItem Command="{Binding DataContext.RemoveCommand}"
                      CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
                      Header="Remove" />
        </ContextMenu>
    </DataGrid.Resources>

    <DataGrid.ItemContainerStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="ContextMenu" Value="{StaticResource ctx_menu}" />
        </Style>
    </DataGrid.ItemContainerStyle>

</DataGrid>

编辑:这会将整个People对象作为CommandParameter。如果您只想要Id,只需将CommandParameter更改为:

CommandParameter="{Binding PlacementTarget.DataContext.Id, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"

答案 1 :(得分:0)

如果你必须将DataMrid中的ContextMenu保留在UI-LIB中,你必须知道,这通常是你的情况下不好的做法。上下文菜单应该放在您需要上下文的位置...那就是您的行。 但无论如何,有一些丑陋的解决方案&#34;。这是一个:

在你的Xaml中,只需绑定Command并忽略CommandParameter。

<DataGrid ItemsSource="{Binding Peoples}">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Command="{Binding RemoveCommand}"
                      Header="Remove" />
        </ContextMenu>
    </DataGrid.ContextMenu>
</DataGrid>

在您的ICommand方法中,您可以这样做:

private void Remove(object obj)
{
    obj = Mouse.DirectlyOver as FrameworkElement;
    if (obj != null)
        obj = ((FrameworkElement)obj).DataContext;

    // obj should be one People here
}

这应该适用于大多数情况,但实际上,你通常会尝试避免这样的事情。