使用MVVM,上下文菜单ViewModel如何找到打开上下文菜单的ViewModel?

8

我正在使用MVVM将视图绑定到树形结构中的对象。我有一个基类,用于实现树形结构中的项,并且该基类具有ContextMenu属性:

    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);

绑定到基类(以及所有派生类)的视图实现了一个上下文菜单,该菜单与该属性绑定:
<ContextMenu x:Name="contextMenu" ItemsSource="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}"
             IsEnabled="{Binding Path=(local:AbstractSolutionItem.ContextMenuEnabled)}"
             ItemContainerStyle="{StaticResource contextMenuStyle}"/>

每个菜单项都绑定到一个IMenuItem对象(菜单项的ViewModel)。当您单击菜单项时,它使用命令在基本对象上执行命令。这一切都很好。
但是,一旦命令在IMenuItem类上执行,它有时需要获取对用户右键单击以弹出上下文菜单的对象的引用(或者至少是该对象的ViewModel)。这就是上下文菜单的全部意义。我应该如何传递树形项ViewModel的引用给MenuItem ViewModel?请注意,某些上下文菜单被树中的许多对象共享。
2个回答

11
ContextMenuStrip对象上有一个名为“PlacementTarget”的DP - 它将被设置为上下文菜单所附加到的UI元素 - 您甚至可以将其用作绑定源,因此您可以通过CommandParameter将其传递给命令:

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

编辑:在您的情况下,您需要PlacementTarget的VM,因此您的绑定可能看起来更像:

{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}

如果使用RelativeSource,则无法指定源。运行时异常。 - Ignacio Soler Garcia
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" - JoanComasFdz

4

我通过在父控件上处理ContextMenuOpening事件(即拥有View中ContextMenu的控件)来解决了这个问题。我还为IMenuItem添加了一个Context属性。处理程序如下:

    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;
        }
    }

这利用了一次只能打开一个上下文菜单的事实。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接