首页 文章

使用MVVM,ContextMenu ViewModel如何找到打开ContextMenu的ViewModel?

提问于
浏览
7

我正在使用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);

绑定到基类(以及所有派生类)的View实现了绑定到该属性的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 回答

  • 4

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

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

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

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

    我通过在父控件(在View中拥有ContextMenu的控件)上处理ContextMenuOpening事件来解决这个问题 . 我还向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;
            }
        }
    

    这利用了一次只能打开一个ContextMenu这一事实 .

相关问题