Using MVVM, how can ContextMenu ViewModel find the ViewModel that ContextMenu opened?

I use MVVM to bind views to objects in a tree. I have a base class that implements elements in my tree, and this base class has the 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); 

A view that binds to the base class (and all derived classes) implements ContextMenu, which binds to this property:

 <ContextMenu x:Name="contextMenu" ItemsSource="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}" IsEnabled="{Binding Path=(local:AbstractSolutionItem.ContextMenuEnabled)}" ItemContainerStyle="{StaticResource contextMenuStyle}"/> 

Each menu item is attached to an IMenuItem object (ViewModel for menu items). When you click on a menu item, it uses commands to execute the command on the base object. All of this works great.

However, as soon as the command is executed in the IMenuItem class, sometimes you need to get a link to the object that the user right-clicked on to bring up the context menu (or ViewModel of this object, at least). This is the whole point of the context menu. How do I apply for passing a link to a ViewModel element of a tree in a MenuItem ViewModel? Note that some context menus are shared by many objects in the tree.

+7
c # wpf mvvm contextmenu
source share
2 answers

I solved this by handling the ContextMenuOpening event on the parent control (the one that owned ContextMenu in the view). I also added a Context property for IMenuItem. The handler is as follows:

  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 exploits the fact that there can only be one ContextMenu at a time.

+4
source share

There, the DP of the ContextMenu object called "PlacementTarget" - this will be configured for the user interface element to which the context menu is connected - you can even use it as a binding source so that you can pass it to your command via CommandParameter:

http://msdn.microsoft.com/en-us/library/system.windows.controls.contextmenu.placementtarget.aspx

edit: in your case, you need a PlacementTarget virtual machine, so your binding will probably be more like:

 {Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}} 
+10
source share

All Articles