使用 MVVM 上下文菜单项命令绑定 WPF

发布于 2024-09-26 00:36:30 字数 2232 浏览 0 评论 0原文

我知道这个问题已经在许多网站和 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
    }   
}

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

I know this question has been asked many times in different ways in many websites and also in StackOverFlow but all the answers I found are not helping me ot to be precise I am not able to understand them and implement in my application. So I thought of putting some code from my application so that you people can help me better.

Problem Statement : I am using a WPF DataGrid. I have added a Context Menu and I have 3 options Cut,Copy,Paste. I am using MVVM for development. I want to DataBind these options to Commands in my ViewModel. But I am unable to do the same. The context menu options are not getting data bound at all !!!

This is my Grid Code in 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">

Then I have a ContextMenu and a Style for Header Element

<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>

This line goes in my constructor

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

This code goes in my AppData class:

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
    }   
}

** I want to know exactly where am I doing wrong?? Why is the DataBinding not happening??
Please help me regarding this. Please provide me the sample code or modification in my present code from which I can achieve it. **

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

和影子一齐双人舞 2024-10-03 00:36:30

您的代码(或当时使用的 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 );

在此处输入图像描述

Something in your code (or the version of WPF being used at the time(?)) is overcomplicating things. I am able to bind such as

<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>

Where the command is setup like this:

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

enter image description here

聚集的泪 2024-10-03 00:36:30

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

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

<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;
}

I had the same issue. The command bindings stopped working once I moved them to the ViewModel from code behind. And in the viewmodel I had to change my ICommand from RoutedCommand to DelegateCommand. I was able to get it working in the following way -

Add Opened eventhandler to your context menu -

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

In the code behind, you would assign your ViewModel to the context menu's DataContext -

private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
    ContextMenu menu = sender as ContextMenu;
    menu.DataContext = _vm;
}
半夏半凉 2024-10-03 00:36:30

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

<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>

有关详细信息,请查看我的文章 实现 MVVM 和 XAML/.NET 应用程序的建议和最佳实践

I usually instantiate my view model as static resource in my view:

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

Then you can easily reference any property of your view model even if the current binding context is not the view model:

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

For more information, have a look at my article Recommendations and best practices for implementing MVVM and XAML/.NET applications.

我做我的改变 2024-10-03 00:36:30
<MenuItem Header="Cut" Command="{Binding Path=PlacementTarget.DataContext.CutCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
<MenuItem Header="Cut" Command="{Binding Path=PlacementTarget.DataContext.CutCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
情话难免假 2024-10-03 00:36:30

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

将 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}"/>

You have two options.
Note: the code samples I added here a similar, but not same as your samples.

Move the definition of the ContextMenu inside the DataGrid defintion as follows:

<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>

Or better add a CommandReference to your Resources and set the Command in the MenuItem to a StaticResource as follows:

<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}"/>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文