【问题标题】:Context Menu items command binding WPF using MVVM使用 MVVM 的上下文菜单项命令绑定 WPF
【发布时间】:2011-04-19 17:59:09
【问题描述】:

我知道这个问题已经在许多网站和 StackOverFlow 中以不同的方式被问过很多次,但我找到的所有答案都没有帮助我准确地说我无法理解它们并在我的应用程序中实施。所以我想从我的应用程序中放一些代码,以便你们可以更好地帮助我。

问题陈述:我正在使用 WPF DataGrid。我添加了一个 上下文菜单,并且有 3 个选项剪切、复制、粘贴。我正在使用 MVVM 进行开发。我想将这些选项数据绑定到我的 ViewModel 中的命令。但我不能这样做。上下文菜单选项根本没有绑定数据!!!

这是我在 XAML 中的网格代码:

<custom:DataGrid  
      x:Name="DataGrid_Standard"   
      Grid.Row="1" Grid.Column="1"   
      AutoGenerateColumns="False"                                                           
      IsSynchronizedWithCurrentItem="True"   
      Background="Transparent"
      ItemsSource="{Binding FullGridData}" 
      ItemContainerStyle="{StaticResource defaultRowStyle}"
      ColumnHeaderStyle="{StaticResource DefaultColumnHeaderStyle}"                         
      Grid.ColumnSpan="2">

然后我有一个 ContextMenu 和一个标题元素的样式

<ContextMenu x:Key="columnHeaderMenu">
   <MenuItem Command="{Binding CutCommand}"
             Header="Test" />
   <MenuItem Header="Copy"/>
   <MenuItem Header="Paste"/>
</ContextMenu>
<Style TargetType="{x:Type custom:DataGridColumnHeader}" x:Key="DefaultColumnHeaderStyle">
    <Setter Property="ContextMenu" Value="{DynamicResource columnHeaderMenu}" >
</Style>

这一行进入我的构造函数

public Window1()
{            
   this.DataContext = new AppData();
}

这段代码在我的 AppData 类中:

public class AppData  
{ 
    private IList<GridData> fullGridData = new ObservableCollection<GridData>();<br> 
    public IList<GridData> FullGridData
    {
        get { return fullGridData; }
        set { fullGridData = value; }
    }

    private DelegateCommand<object> cutCommand;
    public DelegateCommand<object> CutCommand
    {
        get
        {
            if (cutCommand == null)
            {
                cutCommand = new DelegateCommand<object>(CutColumn);
            }
            return cutCommand;
        }
    }

    private void CutColumn(object obj)
    {
        //some code goes here
    }   
}

** 我想确切地知道我在哪里做错了?为什么 DataBinding 没有发生? 请帮助我解决这个问题。请提供我当前代码中的示例代码或修改,我可以从中实现它。 **

【问题讨论】:

    标签: wpf data-binding mvvm command contextmenu


    【解决方案1】:

    我有同样的问题。一旦我将命令绑定从后面的代码移到 ViewModel 中,命令绑定就会停止工作。在视图模型中,我必须将 ICommand 从 RoutedCommand 更改为 DelegateCommand。我能够通过以下方式使其工作 -

    将 Opened 事件处理程序添加到上下文菜单 -

    <ContextMenu x:Key="columnHeaderMenu" Opened="ContextMenu_Opened">
        <MenuItem Command="{Binding CutCommand}" Header="Test" />
        <MenuItem Header="Copy"/>
        <MenuItem Header="Paste"/>
    </ContextMenu>
    

    在后面的代码中,您可以将 ViewModel 分配给上下文菜单的 DataContext -

    private void ContextMenu_Opened(object sender, RoutedEventArgs e)
    {
        ContextMenu menu = sender as ContextMenu;
        menu.DataContext = _vm;
    }
    

    【讨论】:

      【解决方案2】:

      我通常在我的视图中将我的视图模型实例化为静态资源:

      <UserControl x:Class="My.Namespace.MySampleView" ...> 
          <UserControl.Resources> 
              <viewModels:MySampleViewModel x:Key="ViewModel" /> 
          </UserControl.Resources> 
      

      即使当前绑定上下文不是视图模型,您也可以轻松引用视图模型的任何属性:

      <ContextMenu x:Key="columnHeaderMenu">
          <MenuItem Command="{Binding MyCommand, Source={StaticResource ViewModel}}" />
      </ContextMenu>
      

      欲了解更多信息,请查看我的文章Recommendations and best practices for implementing MVVM and XAML/.NET applications

      【讨论】:

      • 这将创建一个与绑定到窗口的 ViewModel 不匹配的新实例!
      【解决方案3】:

      您的代码(或当时使用的 WPF 版本(?))中的某些内容过于复杂。我可以绑定诸如

      <DataGrid AutoGenerateColumns="True"
              Name="myGrid"
              ItemsSource="{Binding Orders}">
          <DataGrid.ContextMenu>
              <ContextMenu>
                  <MenuItem Header="Copy" Command="{Binding CopyItem}" />
                  <MenuItem Header="Delete" Command="{Binding DeleteItem}" />
              </ContextMenu>
          </DataGrid.ContextMenu>
      </DataGrid>
      

      命令设置如下:

      VM.DeleteItem 
           = new OperationCommand((o) => MessageBox.Show("Delete Me"),
                                  (o) => (myGrid.SelectedItem as Order)?.InProgress == false );
      

      【讨论】:

        【解决方案4】:
        <MenuItem Header="Cut" Command="{Binding Path=PlacementTarget.DataContext.CutCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
        

        【讨论】:

          【解决方案5】:

          您有两个选择。
          注意:我在此处添加的代码示例与您的示例相似,但不同。

          将 ContextMenu 的定义移动到 DataGrid 定义中,如下所示:

          <WpfToolkit:DataGrid
             x:Name="DataGrid_Standard"
             IsSynchronizedWithCurrentItem="True"
             Background="Transparent" 
             ItemsSource="{Binding FullGridData}" 
             ColumnHeaderStyle="{StaticResource DefaultColumnHeaderStyle}">
             <WpfToolkit:DataGrid.ContextMenu>
                 <ContextMenu>
                     <MenuItem Command="{Binding CutCommand}" Header="Test" />
                     <MenuItem Header="Copy"/>
                     <MenuItem Header="Paste"/>
                 </ContextMenu>
             </WpfToolkit:DataGrid.ContextMenu>
          </WpfToolkit:DataGrid>
          

          或者最好将 CommandReference 添加到您的资源中,并将 MenuItem 中的 Command 设置为 StaticResource,如下所示:

          <Window.Resources>
              <c:CommandReference x:Key="MyCutCommandReference" Command="{Binding CutCommand}" />
          
              <ContextMenu x:Key="columnHeaderMenu">
                  <MenuItem Command="{StaticResource MyCutCommandReference}" Header="Test" />
                  <MenuItem Header="Copy"/>
                  <MenuItem Header="Paste"/>
              </ContextMenu>
          
              <Style TargetType="{x:Type Primitives:DataGridColumnHeader}" x:Key="DefaultColumnHeaderStyle">
                  <Setter Property="ContextMenu" Value="{DynamicResource columnHeaderMenu}" />
              </Style>
          
          </Window.Resources>
          
          <WpfToolkit:DataGrid
             x:Name="DataGrid_Standard"
             IsSynchronizedWithCurrentItem="True"
             Background="Transparent" 
             ItemsSource="{Binding FullGridData}" 
             ColumnHeaderStyle="{StaticResource DefaultColumnHeaderStyle}"/>
          

          【讨论】:

          • 我正在使用您的第二个解决方案,因为我只想要标题的上下文菜单。实际上现在命令正在关联,但我的菜单项被禁用。我正在使用 DelegateCommand 类进行绑定。真的很奇怪为什么我的菜单项被禁用??
          • 它对我有用,但我不确定你使用的是什么 DelegateCommand 实现;查看控制命令是否启用的第二个参数。我在这里添加了一个示例: cutCommand = new DelegateCommand(CutColumn, CanCutCommand); private bool CanCutCommand(object obj) { return true; }
          • 我正在使用 codeplex 的 mvvm 工具包中提供的 DelegateCommand。我看过一些他们说它的错误的网站。链接如下:connect.microsoft.com/VisualStudio/feedback/details/421816/…social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
          猜你喜欢
          • 2011-09-13
          • 1970-01-01
          • 1970-01-01
          • 2020-05-08
          • 2016-06-04
          • 2018-11-25
          • 1970-01-01
          • 1970-01-01
          • 2012-01-01
          相关资源
          最近更新 更多