【问题标题】:How to bind command to dynamic context menu如何将命令绑定到动态上下文菜单
【发布时间】:2020-11-30 14:25:35
【问题描述】:

我正在使用 c#、prism、wpf。我想在一个listview中动态创建一个上下文列表,如下图:

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

我尝试添加命令标记并绑定到 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()
    {
        
    }

【问题讨论】:

    标签: c# wpf contextmenu prism


    【解决方案1】:

    首先绑定到CommandMenuItem 应该是大括号:{Binding ConfirmButtonCommand}
    其次,你的命令是在MainWindowViewModel类中定义的,而DataContext对于MenuItemMenuNode,所以找不到命令。
    最简单的解决方法是 - 为 List 命名,并在绑定到命令时,参考其 DataContext
    例如:

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

    然后

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

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

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

    【讨论】:

    • 感谢您的回复。我错过了提到我正在使用ViewModelLocationProvider.Register&lt;MainWindowView, MainWindowViewModel&gt;(); 另外,在我添加{} 之后,没有更多的异常抛出,而且也没有对函数HandleConfirmButtonCommand 的回调。另外,函数声明应该是什么?如果我想获取有关谁是呼叫者的信息?由于我是 C# wpf 和 prism 的新手,我真的不知道从哪里开始。谢谢
    • 您是否按照我的建议在MenuItem 中将绑定更改为CommandViewModelLocationProvider 与 wpf 中的 DataContext 无关。要获得来电者 - 按照我的建议添加 CommandParameter 并将您的命令更改为 DelegateCommand&lt;ServiceMode&gt;
    • 顺便说一句,如果您的上下文菜单对于所有 ServiceMode 实例都是相同的并且没有及时更改,那么将上下文菜单移动到 MainWindowViewModel 甚至是 xaml 可能会更好。
    【解决方案2】:

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

    在 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)
        {
            
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-01
      • 2016-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多