I have a ItemsControl
control, in which there is ContextMenu
control.
The ItemsControl
has its ItemsSource
bound to a 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!!!.
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.
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:
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.
I've created a demo project for this problem.
What I have tried
1. Specify Command for MenuItem in a DataTemplate (accepted answer)
<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.
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.
2. Set a relative command
<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
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:
<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.
Ps. this is my first post so if you have any comments then let me know.
1条答案
按热度按时间rqenqsqc1#
好的,第二天我找到了一个基于this解决方案的解决方案。它工作得很好,命令触发器,但是-它更改了上下文菜单的DataContext,因此绑定的
Person
对象不再可用。因此,为了解决这个问题,为了访问当前对象,我在ViewModel中添加了一个Person
属性。每当用户点击一张图片,就会打开一个上下文菜单。这样你就知道点击了什么。不是最preetiy的解决方案,但效果很好。
XAML:
具有Person类别的ViewModel:
如果你有任何问题或意见,让我知道!