如何将命令绑定到动态上下文菜单

How to bind command to dynamic context menu

我正在使用 c#、prism、wpf。我想在listview中动态创建context list,如下图:

当我单击这些菜单项时,将回调到我的自定义函数。在该函数中,我可以识别单击了哪个菜单项,例如,我可以获得菜单项的header。

我尝试添加命令标签并绑定到 ICommand。但是点进去没有反应

我从网上读过不同的例子,但他们从来没有同时显示 xaml 和 viewmodel 的实现。请问怎么办呢?非常感谢。

在App.xaml.cs中:

ViewModelLocationProvider.Register<MainWindowView, MainWindowViewModel>();

以下是我的xaml:

<ListView Grid.Row="2"  ItemsSource="{Binding ServiceObjects}" ItemContainerStyle="{StaticResource LstBoxItemStyleNormal}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <TextBlock Text="{Binding ServiceName}">
                    <TextBlock.ContextMenu>
                        <ContextMenu ItemsSource="{Binding ContextMenuList}">
                            <ContextMenu.ItemTemplate>
                                <HierarchicalDataTemplate>
                                    <MenuItem Header="{Binding MenuName}" Command="{Binding ConfirmButtonCommand}"/>
                                </HierarchicalDataTemplate>
                            </ContextMenu.ItemTemplate>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

以下是我的视图模型:

class MainWindowViewModel : BindableBase
{
    public class MenuNode : BindableBase
    {
        private string _menuName;
        public string MenuName
        {
            get => _menuName;
            set => SetProperty(ref _menuName, value);
        }
    }
    public class ServiceNode : BindableBase
    {
        private string _serviceName;
        public string ServiceName
        {
            get => _serviceName;
            set => SetProperty(ref _serviceName, value);
        }
        public ObservableCollection<MenuNode> ContextMenuList { get; } = new ObservableCollection<MenuNode>();
    }
    public ObservableCollection<ServiceNode> ServiceObjects { get; } = new ObservableCollection<ServiceNode>();
    
    public MainWindowViewModel()
    {
        for (int i = 0; i < 10; i++)
        {
            ServiceNode tempNode = new ServiceNode { ServiceName = "AP", State = "Normal" };
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "A Item" });
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "B Item" });
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "C Item" });
            ServiceObjects.Add(tempNode);
        }
        ConfirmButtonCommand = new DelegateCommand(HandleConfirmButtonCommand);
    }
    public ICommand ConfirmButtonCommand { get; }

    private void HandleConfirmButtonCommand()
    {
        
    }

首先绑定到 Command for MenuItem 应该是大括号:{Binding ConfirmButtonCommand}.
第二,你的命令定义在MainWindowViewModelclass,而DataContext for MenuItemMenuNode,所以找不到命令。
最简单的修复是 - 给 List 命名,当绑定到命令时,参考它的 DataContext.
例如:

<ListView x:Name="list" ...>

然后

<MenuItem Header="{Binding MenuName}" Command="{Binding DataContext.ConfirmButtonCommand, ElementName=list}"/>

此外,对于命令,您可能还需要知道单击了什么 ServiceNode,您可以通过 CommandParameter 传递它(从 [= 获取 DataContext 25=] 包含 MenuItem):

<MenuItem ... CommandParameter="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}">

这是我最后使用的答案。谢谢

在App.xaml.cs中:

ViewModelLocationProvider.Register<MainWindowView, MainWindowViewModel>();

以下是我的xaml:

<ListView Grid.Row="2"  ItemsSource="{Binding ServiceObjects}" ItemContainerStyle="{StaticResource LstBoxItemStyleNormal}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <TextBlock Text="{Binding ServiceName}" Tag="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
                    <TextBlock.ContextMenu>
                        <ContextMenu ItemsSource="{Binding ContextMenuList}">
                            <ContextMenu.ItemTemplate>
                                <HierarchicalDataTemplate>
                                    <MenuItem Header="{Binding MenuName}"
                                              Command="{Binding PlacementTarget.Tag.ConfirmButtonCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
                                              CommandParameter="Binding MenuName"/>
                                </HierarchicalDataTemplate>
                            </ContextMenu.ItemTemplate>
                        </ContextMenu>
                    </TextBlock.ContextMenu>
                </TextBlock>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

以下是我的视图模型:

class MainWindowViewModel : BindableBase
{
    public class MenuNode : BindableBase
    {
        private string _menuName;
        public string MenuName
        {
            get => _menuName;
            set => SetProperty(ref _menuName, value);
        }
    }
    public class ServiceNode : BindableBase
    {
        private string _serviceName;
        public string ServiceName
        {
            get => _serviceName;
            set => SetProperty(ref _serviceName, value);
        }
        public ObservableCollection<MenuNode> ContextMenuList { get; } = new ObservableCollection<MenuNode>();
    }
    public ObservableCollection<ServiceNode> ServiceObjects { get; } = new ObservableCollection<ServiceNode>();
    
    public MainWindowViewModel()
    {
        for (int i = 0; i < 10; i++)
        {
            ServiceNode tempNode = new ServiceNode { ServiceName = "AP", State = "Normal" };
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "A Item" });
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "B Item" });
            tempNode.ContextMenuList.Add(new MenuNode { MenuName = "C Item" });
            ServiceObjects.Add(tempNode);
        }
        ConfirmButtonCommand = new DelegateCommand<string>(HandleConfirmButtonCommand);
    }
    public ICommand ConfirmButtonCommand { get; }

    private void HandleConfirmButtonCommand(string parameter)
    {
        
    }