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.