如何将 ViewModel(包括分隔符)正确绑定到 WPF 的菜单? [英] How to correctly bind a ViewModel (which Include Separators) to WPF's Menu?
问题描述
我正在使用 MVVM 并且我想将我的 MenuViewModels
列表数据绑定到我的主要菜单.其中由一组菜单项和分隔符组成.
I'm using MVVM and I want to data bind my list of MenuViewModels
to my maim menu. Which consists of a set of menu items and separators.
这是我的 MenuItemViewModel 代码:
Here's my MenuItemViewModel code:
public interface IMenuItemViewModel
{
}
[DebuggerDisplay("---")]
public class SeparatorViewModel : IMenuItemViewModel
{
}
[DebuggerDisplay("{Header}, Children={Children.Count}")]
public class MenuItemViewModel : IMenuItemViewModel, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuItemViewModel(string header, ICommand command, ImageSource imageSource)
{
Header = header;
Command = command;
ImageSource = imageSource;
Children = new List<IMenuItemViewModel>();
}
public string Header { get; private set; }
public ICommand Command { get; private set; }
public ImageSource ImageSource { get; private set; }
public IList<IMenuItemViewModel> Children { get; private set; }
}
我的主窗口如下所示:
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}">
</Menu>
</DockPanel>
应该是非常简单的东西.不幸的是,要么菜单项看起来不对,要么分隔符是一个空的 menuItem
(取决于我的尝试).
Should be very simple stuff. Unfortunately, either the menu item looks wrong or the separator is an empty menuItem
(depending on what I've tried).
那么,如何让我的 Menu
找到我的两个 DataTemplates
?
So, how do I get my Menu
to find my two DataTemplates
?
推荐答案
解决了我自己的问题
在网上搜索了几个小时后,我发现了很多违背 WPF 的自然意图的例子,但没有一个能与它一起工作.
After spending several hours searching the web, I found lots of examples that work against the WPF's natural intentions but none that worked with it.
这里是如何使用Menu
控件而不是反对它...
Here's how to work with the Menu
control and not against it...
一点背景
WPF 的 Menu
控件将通常自动创建 MenuItem
对象,当它绑定到 POCO 集合时,使用 ItemsSource
属性.
WPF's Menu
control will normally auto create MenuItem
objects for you when it is binded to a POCO collection, using the ItemsSource
property.
但是,这个默认行为可以被覆盖!方法如下...
However, this default behavior can be overridden! Here's how...
解决方案
首先,您必须创建一个派生自 ItemContainerTemplateSelector
的类.或者使用我创建的简单类:
First, you must create a class that derives from ItemContainerTemplateSelector
. Or use the simple class I've created:
public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var key = new DataTemplateKey(item.GetType());
return (DataTemplate) parentItemsControl.FindResource(key);
}
}
其次,您必须向 Windows resources
对象添加对 MenuItemContainerTemplateSelector
类的引用,如下所示:
Second, you must add a reference to the MenuItemContainerTemplateSelector
class to your Windows resources
object, like so:
<Window.Resources>
<Selectors:MenuItemContainerTemplateSelector x:Key="_menuItemContainerTemplateSelector" />
第三,您必须在 Menu
和 MenuItem
上设置两个属性(UsesItemContainerTemplate
和 ItemContainerTemplateSelector
)>(在 HierarchicalDataTemplate
中定义).
Third, you must set two properties (UsesItemContainerTemplate
, and ItemContainerTemplateSelector
) on both the Menu
and the MenuItem
(which is defined in the HierarchicalDataTemplate
).
像这样:
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Header}"
Command="{Binding Command}"
UsesItemContainerTemplate ="true"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}"/>
</HierarchicalDataTemplate>
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding MenuItems}"
UsesItemContainerTemplate="True"
ItemContainerTemplateSelector=
"{StaticResource _menuItemContainerTemplateSelector}">
</Menu>
为什么有效
出于优化目的,Menu
使用 UsesItemContainerTemplate
标志(默认值为 false
)来跳过 DataTemplate
查找并返回一个普通的 MenuItem
对象.因此,我们需要将此值设置为 true
,然后我们的 ItemContainerTemplateSelector
按预期工作.
For optimization purposes, the Menu
uses the UsesItemContainerTemplate
flag (which has a default value of false
) to skip the DataTemplate
lookup and just returns a normal MenuItem
object. Therefore, we needed to set this value to true
and then our ItemContainerTemplateSelector
works as expected.
快乐编码!
这篇关于如何将 ViewModel(包括分隔符)正确绑定到 WPF 的菜单?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!