如何将ViewModel(包括分隔符)正确绑定到WPF';s菜单
本文关键字:WPF 绑定 菜单 ViewModel 包括 分隔符 | 更新日期: 2023-09-27 18:01:09
我正在使用MVVM,我想将我的MenuViewModels
列表数据绑定到我的主菜单。它由一组菜单项和分隔符组成。
这是我的MenuItemViewModel代码:
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
(取决于我尝试过的内容(。
那么,我如何让我的Menu
找到我的两个DataTemplates
呢?
解决了我自己的问题
花了几个小时在网上搜索后,我发现了很多与WPF的自然意图背道而驰的例子,但没有一个能奏效。
以下是如何使用Menu
控件而不是与之对抗…
一点背景
WPF的Menu
控件在绑定到POCO集合时,通常会使用ItemsSource
属性为您自动创建MenuItem
对象。
但是,可以覆盖此默认行为!以下是如何。。。
解决方案
首先,必须创建一个从ItemContainerTemplateSelector
派生的类。或者使用我创建的简单类:
public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector
{
public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl)
{
var key = new DataTemplateKey(item.GetType());
return (DataTemplate) parentItemsControl.FindResource(key);
}
}
其次,您必须将对MenuItemContainerTemplateSelector
类的引用添加到Windows resources
对象中,如下所示:
<Window.Resources>
<Selectors:MenuItemContainerTemplateSelector x:Key="_menuItemContainerTemplateSelector" />
第三,必须在Menu
和MenuItem
(在HierarchicalDataTemplate
中定义(上设置两个属性(UsesItemContainerTemplate
和ItemContainerTemplateSelector
(。
像这样:
<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
才能按预期工作。
快乐编码!
没有TemplateSelector:的解决方案
提供ItemContainerTemplate而不是DataTemplates:
<ContextMenu ItemsSource="{Binding Path=MenuItems}" UsesItemContainerTemplate="True">
<ContextMenu.Resources>
<ResourceDictionary>
<ItemContainerTemplate DataType="{x:Type ViewModel:MenuItemViewModel }">
<MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}" UsesItemContainerTemplate="True">
<MenuItem.Icon>
<Image Source="{Binding Path=ImageSource}"/>
</MenuItem.Icon>
</MenuItem>
</ItemContainerTemplate>
<ItemContainerTemplate DataType="{x:Type ViewModel:SeparatorViewModel}">
<Separator >
<Separator.Style>
<Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/>
</Separator.Style>
</Separator>
</ItemContainerTemplate>
</ResourceDictionary>
</ContextMenu.Resources>
</ContextMenu>
注:
- 我还没试过孩子
- 分隔符样式错误:我不得不手动重新应用该样式
另一种方法是:
- 在菜单项ViewModel上具有布尔属性,该属性指示项是否为分隔符
- 使用基于此属性的触发器更改
MenuItem
的ControlTemplate
,使其改用Separator
控件
像这样:
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Command}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeparator}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Separator />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}"
ItemsSource="{Binding Children}" />
</Menu.Resources>
</Menu>