[英]Bind a ContextMenu Command inside of a ItemSource to a command inside the ViewModel
我有一个ItemsControl
控件,其中有ContextMenu
控件。
ItemsControl
将其ItemsSource
绑定到List<Person>
。
我想要做的是将DisplayNameCommand
和DisplaySurnameCommand
绑定到它们相应的上下文菜单项,其中两个命令都在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'.
接受的答案首先绑定到一个人 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.