Using MVVM, how can a ContextMenu ViewModel find the ViewModel that opened the ContextMenu?
c#, contextmenu, mvvm, wpf
Solution
I solved this by handling the ContextMenuOpening event on the parent control (the one that owned the ContextMenu in the View). I also added a Context property to IMenuItem. The handler looks like this:
private void stackPanel_ContextMenuOpening(
object sender, ContextMenuEventArgs e)
{
StackPanel sp = sender as StackPanel;
if (sp != null)
{
// solutionItem is the "context"
ISolutionItem solutionItem =
sp.DataContext as ISolutionItem;
if (solutionItem != null)
{
IEnumerable<IMenuItem> items =
solutionItem.ContextMenu as IEnumerable<IMenuItem>;
if (items != null)
{
foreach (IMenuItem item in items)
{
// will automatically set all
// child menu items' context as well
item.Context = solutionItem;
}
}
else
{
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
This takes advantage of the fact that there can only be one ContextMenu open at a time.
Problem
I'm using MVVM to bind views to objects in a Tree. I have a base class that implements the items in my tree, and that base class has a ContextMenu property: ``` public IEnumerable<IMenuItem> ContextMenu { get { return m_ContextMenu; } protected set { if (m_ContextMenu != value) { m_ContextMenu = value; NotifyPropertyChanged(m_ContextMenuArgs); } } } private IEnumerable<IMenuItem> m_ContextMenu = null; static readonly PropertyChangedEventArgs m_ContextMenuArgs = NotifyPropertyChangedHelper.CreateArgs<AbstractSolutionItem>(o => o.ContextMenu); ``` The View that binds to the base class (and all derived classes) implements a ContextMenu that binds to that property: ``` <ContextMenu x:Name="contextMenu" ItemsSource="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}" IsEnabled="{Binding Path=(local:AbstractSolutionItem.ContextMenuEnabled)}" ItemContainerStyle="{StaticResource contextMenuStyle}"/> ``` Each item in the menu is bound to an IMenuItem object (a ViewModel for the menu items). When you click on the menu item, it uses Commands to execute a command on the base object. This all works great. However, once the command is executing on the IMenuItem class, it sometimes needs to get a reference to the object that the user right clicked on to bring up the context menu (or the ViewModel of that object, at least). That's the whole point of a context menu. How should I go about passing the reference of the tree item ViewModel to the MenuItem ViewModel? Note that some context menus are shared by many objects in the tree.