简体   繁体   English

将 ItemSource 内的 ContextMenu 命令绑定到 ViewModel 内的命令

[英]Bind a ContextMenu Command inside of a ItemSource to a command inside the ViewModel

I have a ItemsControl control, in which there is ContextMenu control.我有一个ItemsControl控件,其中有ContextMenu控件。
The ItemsControl has its ItemsSource bound to a List<Person> . ItemsControl将其ItemsSource绑定到List<Person>

What I want to do is to bind a DisplayNameCommand and a DisplaySurnameCommand to their corresponding context menu item , where both commands are inside of a MainWindowViewModel - not the bound Person object!!!我想要做的是将DisplayNameCommandDisplaySurnameCommand绑定到它们相应的上下文菜单项,其中两个命令都在MainWindowViewModel内 -而不是绑定的Person对象!!! . .

The important thing is that ContextMenu needs to still have the ItemsSource data context, as I need to acces the bound object property to include it in the command parameter.重要的是ContextMenu仍然需要具有ItemsSource数据上下文,因为我需要访问绑定的 object 属性以将其包含在命令参数中。

ItemsControl:项目控制:

<ItemsControl ItemsSource="{Binding PeopleList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="{Binding Surname}"/>
                <Image>
                    <Image.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="Display Name" 
                                        Command="{Binding DisplaySurnameCommand}" 
                                        CommandParameter="{Binding Name}">
                            </MenuItem>
                            <MenuItem Header="Display Surname" 
                                        Command="{Binding DisplaySurnameCommand}" 
                                        CommandParameter="{Binding Surname}">
                            </MenuItem>
                        </ContextMenu>
                    </Image.ContextMenu>
                </Image>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

MainWindowViewModel and Person class: MainWindowViewModel 和 Person class:

    public class MainWindowViewModel
    {
        public MainWindowViewModel()
        {
            DisplayNameCommand = new RelayCommand(DisplayName);
            DisplaySurnameCommand = new RelayCommand(DisplaySurname);

            PeopleList = new List<Person>();

            PeopleList.Add(new Person("Julie", "Adams"));
            PeopleList.Add(new Person("Mack", "McMack"));
            PeopleList.Add(new Person("Josh", "Broccoli"));
        }

        public List<Person> PeopleList { get; set; }

        public void DisplayName(object message)
        {
            MessageBox.Show("Name: " + (string)message);
        }

        public void DisplaySurname(object message)
        {
            MessageBox.Show("Surname: "+ (string)message);
        }

        public RelayCommand DisplayNameCommand { get; }
        public RelayCommand DisplaySurnameCommand { get; }
    }

    public class Person
    {
        public Person(string name, string surname)
        {
            Name = name;
            Surname = surname;
        }

        public string Name { get; set; }
        public string Surname { get; set; }
    }

Also I know that it is possible to bind it to a Person object and then point it to a viewmodel command, but that's not what I'm looking for.我也知道可以将它绑定到Person object,然后将其指向 viewmodel 命令,但这不是我想要的。

I've created a demo project for this problem.我为这个问题创建了一个演示项目

What I have tried我试过的

1. Specify Command for MenuItem in a DataTemplate (accepted answer) 1. 在 DataTemplate 中为 MenuItem 指定命令(已接受的答案)

<ContextMenu>
    <ContextMenu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding DataContext.DisplaySurname, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
            <Setter Property="CommandParameter" Value="{Binding Name}"/>
        </Style>
    </ContextMenu.ItemContainerStyle>

    <MenuItem Header="Display Name">
    <MenuItem Header="Display Surname">
</ContextMenu>

So this is the closest that I got to the result, this does trigger the command but the problem is that there can be only 1 command set for all menu items.所以这是我得到的最接近结果的结果,这确实触发了命令,但问题是所有菜单项只能有 1 个命令集。
If there could be a way to get around this by setting the name for the style or using something else than ItemContainerStyle it could work, but I couldn't come up with anything like that.如果有办法通过设置样式的名称或使用 ItemContainerStyle 以外的其他东西来解决这个问题,它可以工作,但我想不出类似的东西。

2. Set a relative command 2.设置相对命令

<ContextMenu>
    <MenuItem Header="Display Name"
                Command="{Binding DataContext.DisplayNameCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
                CommandParameter="{Binding Name}"/>
    <MenuItem Header="Display Surname"
                Command="{Binding DataContext.DisplaySurnameCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"
                CommandParameter="{Binding Surname}"/>
</ContextMenu>

This returns a binding error:这将返回一个绑定错误:
Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'.

3. Can't bind a ContextMenu action to a Command 3. 无法将 ContextMenu 操作绑定到命令

The accepted answer first bound to a person object, and the updated solution didn't even work, but the second answer that I've tried:接受的答案首先绑定到一个人 object,更新的解决方案甚至不起作用,但我尝试过的第二个答案:

<MenuItem Header="Display Surname"
            Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DisplaySurnameCommand}"
            CommandParameter="{Binding Surname}"/>

returned a binding error of:返回以下绑定错误:
Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1'.

... and besides those three I've tried many more soltuions and variations, with little to no result. ...除了这三个之外,我还尝试了更多的解决方案和变体,但几乎没有结果。

I've spent a whole day trying to find a solution to this, please help me, wpf is taking away my sanity.我花了一整天的时间试图找到解决方案,请帮助我,wpf 正在夺走我的理智。

Ps.附言。 this is my first post so if you have any comments then let me know.这是我的第一篇文章,所以如果您有任何意见,请告诉我。

Alright, on the next day I found a workaround to this based on this solution.好的,第二天我找到了基于解决方案的解决方法。 And it works great, the comamnds trigger, but - it changes the DataContext of the context menu , so the bound Person object is no longer available.它工作得很好,comamnds 触发器,但是 - 它改变了上下文菜单的 DataContext ,所以绑定的Person object 不再可用。 So, to get around this, to acces the current object I've added a Person property inside of the ViewModel, that updates whenever the user clicks a image that opens a context menu.因此,为了解决这个问题,为了访问当前的 object,我在 ViewModel 中添加了一个Person属性,每当用户单击打开上下文菜单的图像时,该属性就会更新 So in that way you know what item has been clicked.所以这样你就知道点击了什么项目。

This solution will work to both acces the bound object, and will work on any number of commands.此解决方案既可以访问绑定的 object,也可以处理任意数量的命令。
Not the preetiest solution, but it works great.不是最漂亮的解决方案,但效果很好。

Working project demo 工作项目演示

Xaml: Xaml:

<ItemsControl ItemsSource="{Binding PeopleList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="{Binding Surname}"/>
                <Image Source="more_64px.png" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}" >
                    <Image.InputBindings>
                        <MouseBinding Gesture="LeftClick" Command="{Binding DataContext.UpdateCurrentObjectCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" CommandParameter="{Binding}"/>
                        <MouseBinding Gesture="RightClick" Command="{Binding DataContext.UpdateCurrentObjectCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}" CommandParameter="{Binding}"/>
                    </Image.InputBindings>
                    <Image.ContextMenu>
                        <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={x:Static RelativeSource.Self}}">
                            <MenuItem Header="Display Name" Command="{Binding DisplayNameCommand}"/>
                            <MenuItem Header="Display Surname" Command="{Binding DisplaySurnameCommand}"/>
                        </ContextMenu>
                    </Image.ContextMenu>
                </Image>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

ViewModel with Person class: ViewModel 与人 class:

public class MainWindowViewModel
    {
        public MainWindowViewModel()
        {
            DisplayNameCommand = new RelayCommand(DisplayName);
            DisplaySurnameCommand = new RelayCommand(DisplaySurname);

            PeopleList = new List<Person>();

            PeopleList.Add(new Person("Julie", "Adams"));
            PeopleList.Add(new Person("Mack", "McMack"));
            PeopleList.Add(new Person("Josh", "Broccoli"));

            UpdateCurrentObjectCommand = new RelayCommand(UpdateCurrentObject);
        }

        private void UpdateCurrentObject(object person)
        {
            CurrentPerson = (Person)person;
        }

        private Person CurrentPerson { get; set; }

        public List<Person> PeopleList { get; set; }

        public void DisplayName()
        {
            MessageBox.Show("Name: " + CurrentPerson.Name);
        }

        public void DisplaySurname()
        {
            MessageBox.Show("Name: " + CurrentPerson.Surname);
        }

        public RelayCommand DisplayNameCommand { get; }
        public RelayCommand DisplaySurnameCommand { get; }
        public RelayCommand UpdateCurrentObjectCommand { get; }
    }

    public class Person
    {
        public Person(string name, string surname)
        {
            Name = name;
            Surname = surname;
        }

        public string Name { get; set; }
        public string Surname { get; set; }
    }

If you have any questions or comments, lemme know!如果您有任何问题或意见,请告诉我!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM