繁体   English   中英

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

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

我有一个ItemsControl控件,其中有ContextMenu控件。
ItemsControl将其ItemsSource绑定到List<Person>

我想要做的是将DisplayNameCommandDisplaySurnameCommand绑定到它们相应的上下文菜单项,其中两个命令都在MainWindowViewModel内 -而不是绑定的Person对象!!! .

重要的是ContextMenu仍然需要具有ItemsSource数据上下文,因为我需要访问绑定的 object 属性以将其包含在命令参数中。

项目控制:

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

我也知道可以将它绑定到Person object,然后将其指向 viewmodel 命令,但这不是我想要的。

我为这个问题创建了一个演示项目

我试过的

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>

所以这是我得到的最接近结果的结果,这确实触发了命令,但问题是所有菜单项只能有 1 个命令集。
如果有办法通过设置样式的名称或使用 ItemContainerStyle 以外的其他东西来解决这个问题,它可以工作,但我想不出类似的东西。

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>

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

3. 无法将 ContextMenu 操作绑定到命令

接受的答案首先绑定到一个人 object,更新的解决方案甚至不起作用,但我尝试过的第二个答案:

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

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

...除了这三个之外,我还尝试了更多的解决方案和变体,但几乎没有结果。

我花了一整天的时间试图找到解决方案,请帮助我,wpf 正在夺走我的理智。

附言。 这是我的第一篇文章,所以如果您有任何意见,请告诉我。

好的,第二天我找到了基于解决方案的解决方法。 它工作得很好,comamnds 触发器,但是 - 它改变了上下文菜单的 DataContext ,所以绑定的Person object 不再可用。 因此,为了解决这个问题,为了访问当前的 object,我在 ViewModel 中添加了一个Person属性,每当用户单击打开上下文菜单的图像时,该属性就会更新 所以这样你就知道点击了什么项目。

此解决方案既可以访问绑定的 object,也可以处理任意数量的命令。
不是最漂亮的解决方案,但效果很好。

工作项目演示

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

如果您有任何问题或意见,请告诉我!

暂无
暂无

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

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