【问题标题】:Can I apply a ContextMenu to a ContextMenuViewModel using a DataTemplate?我可以使用 DataTemplate 将 ContextMenu 应用于 ContextMenuViewModel 吗?
【发布时间】:2010-02-21 22:10:48
【问题描述】:

我有一个 ViewModel (AbstractContextMenu) 代表我的上下文菜单 (IContextMenu),我使用 DataTemplate 将一个真正的 ContextMenu 绑定到它:

<DataTemplate DataType="{x:Type local:AbstractContextMenu}">
    <ContextMenu x:Name="contextMenu" 
          ItemsSource="{Binding Path=(local:IContextMenu.Items)}"
          IsEnabled="{Binding Path=(local:IContextMenu.IsEnabled)}"/>
</DataTemplate>

然后我有一个虚拟的 ConcreteContextMenu 用于测试,它只是继承自 AbstractContextMenu。 AbstractContextMenu 只是实现了这个接口:

public interface IContextMenu : IExtension
{
    IEnumerable<IMenuItem> Items { get; set; }
    bool IsEnabled { get; set; }
}

我将它用作另一个 ViewModel 对象的属性:

    public IContextMenu ContextMenu
    {
        get
        {
            return m_ContextMenu;
        }
        protected set
        {
            if (m_ContextMenu != value)
            {
                m_ContextMenu = value;
                NotifyPropertyChanged(m_ContextMenuArgs);
            }
        }
    }
    private IContextMenu m_ContextMenu = new ConcreteContextMenu();
    static readonly PropertyChangedEventArgs m_ContextMenuArgs =
        NotifyPropertyChangedHelper.CreateArgs<AbstractSolutionItem>(o => o.ContextMenu);

然后我将 StackPanel 绑定到该 ViewModel 并将 StackPanel 上的 ContextMenu 属性绑定到 ViewModel 的 ContextMenu 属性:

    <StackPanel Orientation="Horizontal" 
                ContextMenu="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}"
                ContextMenuOpening="stackPanel_ContextMenuOpening">
    <!-- stuff goes in here -->
    </StackPanel>

当我运行它时,会触发 StackPanel 上的 ContextMenuOpening 事件,但不会显示 ContextMenu。我不确定我是否能做到这一点(使用 DataTemplate 将 ContextMenu 应用于 ContextMenu ViewModel)。有人知道吗?

【问题讨论】:

    标签: wpf mvvm datatemplate contextmenu


    【解决方案1】:

    AbstractSolutionItem.ContextMenu 的类型是什么?如果它对应于您问题中的 ContextMenu 属性,那么问题可能是类型错误。 FrameworkElementContextMenu 属性期待实际的 ContextMenu,而不是 IContextMenu。在调试您的应用时尝试检查输出窗口 - 您可能会收到一条错误消息,指出这是问题所在。

    不要使用DataTemplate 来定义您的ContextMenu,只需将模板的内容放入StackPanel.ContextMenu

    <StackPanel Orientation="Horizontal" 
        ContextMenu="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}"
        ContextMenuOpening="stackPanel_ContextMenuOpening">
        <StackPanel.ContextMenu DataContext="{Binding Path=(local:AbstractSolutionItem.ContextMenu)}">
            <ContextMenu x:Name="contextMenu" 
                ItemsSource="{Binding Path=Items}"
                IsEnabled="{Binding Path=IsEnabled}"/>
        </StackPanel.ContextMenu>
        <!-- stuff goes in here -->
    </StackPanel>
    

    这应该可以让你大部分时间到达那里。但是,仍然存在一个问题,因为ContextMenu 不知道如何从IMenuItem 创建MenuItem。为了解决这个问题,为ContextMenu 创建一个ItemTemplate,它将IMenuItem 的成员绑定到`MenuItem。

    【讨论】:

    • 嗨,安迪。 WPF 可以识别我为 AbstractMenuItem 定义的 DataTemplate,它确实可以很好地创建菜单(毕竟这是 MVVM 的核心),但正如你所说,似乎我不能只将 StackPanel.ContextMenu 属性绑定到随机对象并让 WPF 将我的 DataTemplate 应用到它。我希望有办法解决这个问题,但看起来我的选择是零。谢谢。太糟糕了 - 我正在尝试使用完整的 MVVM,但这很难。
    • 这并没有真正解决我的问题,但看起来我想做的事情没有解决方案,它是“正确的”。 ;)
    【解决方案2】:

    您能否解释一下DataTemplateItemsSource 属性中使用的语法?使用括号通常意味着附加属性。而Items 似乎不是IContextMenu 定义的附加属性(因为接口不能定义这样的属性)。

    DataTemplate 链接到AbstractContextMenu 类型的对象,该对象具有名为Items 的属性。因此,DataTemplate 可以像这样简单地引用它:

    <DataTemplate DataType="{x:Type local:AbstractContextMenu}">
        <ContextMenu x:Name="contextMenu" 
              ItemsSource="{Binding Path=Items)}"
              IsEnabled="{Binding Path=IsEnabled}"/>
    </DataTemplate>
    

    如果AbstractSolutionItem类是StackPanel的VM,你可以这样绑定它:

    <StackPanel Orientation="Horizontal" 
                ContextMenu="{Binding Path=ContextMenu}"
                ContextMenuOpening="stackPanel_ContextMenuOpening">
    <!-- stuff goes in here -->
    </StackPanel>
    

    当然,DataTemplate 必须可以从 StackPanel “访问”。

    【讨论】:

    • 您可能不熟悉这种绑定语法,但它的工作方式与您所描述的完全相同,只是它更高效,因为它可以在编译时而不是运行时解析。我忘记了语法的名称,但我尽可能改用它,因为使用 MVVM 时,您会做很多绑定和性能问题。
    【解决方案3】:

    将视图的 ContextMenu 属性(在本场景中为 StackPanel)绑定到 ViewModel 的 ContextMenu 属性,并为将创建 ContextMenu 对象并将 IContextMenu 设置为它的 DataContext 的绑定提供 IValueConverter。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多