我有一个场景,我需要同时拥有静态和动态菜单项。静态项目将在XAML中定义,动态的项目由View Model提供。每个动态项本身都由VieModel表示,我们称之为CommandViewModel。 CommandViewModel除其他外还包含显示名称,它还可以包含其他CommandViewModel。
用作菜单的datacontext的MainViewModel如下:
public class MainMenuViewModel : INotifyPropertyChanged
{
private ObservableCollection<CommandViewModel> m_CommandVMList;
public MainMenuViewModel()
{
m_ CommandVMList = new ObservableCollection<CommandViewModel>();
CommandViewModel cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 1";
m_CommandVMList.Add(cmv);
cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 2";
m_CommandVMList.Add(cmv);
cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 3";
m_CommandVMList.Add(cmv);
}
public ObservableCollection<CommandViewModel> CommandList
{
get { return m_CommandVMList; }
set
{
m_CommandVMList = value;
OnPropertyChanged("CommandList");
}
}
... ... ...
菜单XAML:
<Grid>
<Grid.Resources>
<HierarchicalDataTemplate DataType="{x:Type Fwf:CommandViewModel}" ItemsSource="{Binding Path=CommandViewModels}">
<MenuItem Header="{Binding Path=DisplayName}"/>
</HierarchicalDataTemplate>
</Grid.Resources>
<Menu VerticalAlignment="Top" HorizontalAlignment="Stretch">
<MenuItem Header="Static Top Menu Item 1">
<MenuItem Header="Static Menu Item 1"/>
<MenuItem Header="Static Menu Item 2"/>
<MenuItem Header="Static Menu Item 3"/>
<ItemsControl ItemsSource="{Binding Path= CommandList}"/>
<MenuItem Header="Static Menu Item 4"/>
</MenuItem>
</Menu>
</Grid>
一切正常,除了我试图表示动态菜单列表之外,在这种情况下,它在UI上显示为包含更多菜单项的ONE菜单项,因此选择了整个动态菜单项集合当您单击该项目时。正确表示集合,因为每个动态菜单项都显示为菜单项本身,但在此更大的菜单项中。我想我明白为什么因为菜单只是为每个包含的项目创建一个菜单项,静态或动态它并不关心。有没有办法让每个动态菜单项在同一级别创建并属于父菜单项,就像示例中的静态菜单项一样?
答案 0 :(得分:5)
我不会在XAML端硬编码“静态”菜单项,而是将它们作为CommandViewModel对象在VM端进行硬编码。
由于您无论采用哪种方式进行硬编码,都不会失去灵活性,如果您选择在将来以不同方式呈现静态菜单项,您将获得保持静态菜单项与HierarchicalDataTemplate同步的额外好处。
请注意,您可能需要更改绑定,以便菜单绑定到一组菜单项。您可以找到此here的示例。
编辑:代码示例
我能够相当快地破解它,并且大多数类定义都是不完整的(例如INotifyPropertyChanged),但它应该让你知道你可以做什么。我在第三个命令上添加了一些命令嵌套,以确保Hierarchical DataTemplate正常工作。
这是XAML
<Window
x:Class="WPFDynamicMenuItems.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFDynamicMenuItems"
Title="Window1" Height="300" Width="600">
<Grid>
<Grid.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:CommandViewModel}" ItemsSource="{Binding Path=CommandList}">
<ContentPresenter
Content="{Binding Path=DisplayName}"
RecognizesAccessKey="True" />
</HierarchicalDataTemplate>
</Grid.Resources>
<ToolBarTray>
<ToolBar>
<Menu>
<Menu.ItemsSource>
<CompositeCollection>
<MenuItem Header="A"></MenuItem>
<MenuItem Header="B"></MenuItem>
<MenuItem Header="C"></MenuItem>
<CollectionContainer x:Name="dynamicMenuItems">
</CollectionContainer>
<MenuItem Header="D"></MenuItem>
</CompositeCollection>
</Menu.ItemsSource>
</Menu>
</ToolBar>
</ToolBarTray>
</Grid>
</Window>
这是代码隐藏的代码:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WPFDynamicMenuItems
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private MainMenuViewModel _mainMenuVM = new MainMenuViewModel();
public Window1()
{
InitializeComponent();
this.dynamicMenuItems.Collection = this._mainMenuVM.CommandList;
}
}
public class MainMenuViewModel : INotifyPropertyChanged
{
private ObservableCollection<CommandViewModel> m_CommandVMList;
public MainMenuViewModel()
{
m_CommandVMList = new ObservableCollection<CommandViewModel>();
CommandViewModel cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 1";
m_CommandVMList.Add(cmv);
cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 2";
m_CommandVMList.Add(cmv);
cmv = new CommandViewModel();
cmv.DisplayName = "Dynamic Menu 3";
m_CommandVMList.Add(cmv);
CommandViewModel nestedCMV = new CommandViewModel();
nestedCMV.DisplayName = "Nested Menu 1";
cmv.CommandList.Add(nestedCMV);
nestedCMV = new CommandViewModel();
nestedCMV.DisplayName = "Nested Menu 2";
cmv.CommandList.Add(nestedCMV);
}
public ObservableCollection<CommandViewModel> CommandList
{
get { return m_CommandVMList; }
set { m_CommandVMList = value; OnPropertyChanged("CommandList"); }
}
protected void OnPropertyChanged(string propertyName)
{
// Hook up event...
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class CommandViewModel : INotifyPropertyChanged
{
private ObservableCollection<CommandViewModel> m_CommandVMList;
public CommandViewModel()
{
this.m_CommandVMList = new ObservableCollection<CommandViewModel>();
}
public string DisplayName { get; set; }
public ObservableCollection<CommandViewModel> CommandList
{
get { return m_CommandVMList; }
set { m_CommandVMList = value; OnPropertyChanged("CommandList"); }
}
protected void OnPropertyChanged(string propertyName)
{
// Hook up event...
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}