题
我是WPF和MVVM的新手。我已经搜索了一个在MVVM parttern中动态创建菜单的好方法,我找不到任何我想要的东西,所以我推出了自己的解决方案。它有效,但由于某种原因,菜单的前景(文本)颜色有时(有时)不正确。
我为下面的图片添加了一个链接。
http://img220.imageshack.us/img220/1912/ badmenu.jpg (死链接)
我的最低子菜单正确显示白色前景,但其父菜单forground变为黑色,几乎无法读取。如果我对菜单进行了硬编码,则父母的forground颜色将为白色。如果我将鼠标移到父项上,其文本将切换回白色,子菜单将变为黑色。
此外,一旦我将鼠标移离父母,其所有布尔属性 IsHighlighted,IsSubmenuOpen等...
都会变为false,这让我感到惊讶,因为我认为它们应该保持真实。最终的结果是我无法用样式触发器来解决这个问题。
这是我的XAML。
<Window.Resources>
<DataTemplate DataType="{x:Type src:ParentMenu}" >
<Menu >
<MenuItem Header="{Binding MenuName}" ItemsSource="{Binding ChildMenuItems}" />
</Menu>
</DataTemplate>
<HierarchicalDataTemplate DataType="{x:Type src:ChildMenu}"
ItemsSource="{Binding ChildMenuItems}" >
<MenuItem Header="{Binding MenuName}" Command="{Binding Path=Command}" />
</HierarchicalDataTemplate>
'StackOverflow正在屏蔽Window.Resources的结束标记
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Menus}" />
<Grid>
<!-- Add additional content here -->
</Grid>
</DockPanel>
ParentMenu
和 ChildMenu
都从一个实际拥有所有菜单的公共类继承,并通过 ChildMenuItems
集合公开子菜单。 ChildMenuItems
是 ChildMenu
对象的列表。我的 ViewModels
公开了 ParentMenu
对象的列表。
可能有更好的方法来完成我想要的东西。这是一个例子:
img132.imageshack.us/img132/4160/bettermenu.jpg (死链接)
关于我做错了什么和/或如何解决显示问题的任何建议?
解决方案
问题是您的VM会自动包装在MenuItems中,因此您基本上将MenuItems嵌套为MenuItems的标题。
你可以通过使用MenuItem作为数据类型定义一个Style(并通过ItemContainerStyle指向它)来解决这个问题,这个样式绑定到你的VM(Name to Header,DelegateCommands to Command等)。
您可以执行此操作的示例如下所示。请注意,我删除了HierarchicalDataTemplate以支持ItemContainerStyle。我也冒昧地为你的MainViewModel定义一个DataTemplate,因为它不是很清楚数据绑定是什么。
<Window.Resources>
<DataTemplate DataType="{x:Type src:MainViewModel}">
<ItemsControl ItemsSource="{Binding Menus}"></ItemsControl>
</DataTemplate>
<DataTemplate DataType="{x:Type src:ParentMenu}" >
<Menu>
<MenuItem Header="{Binding Name}"
ItemsSource="{Binding ChildMenuItems}" ItemContainerStyle="{DynamicResource ChildMenuItemStyle}" />
</Menu>
</DataTemplate>
<Style x:Key="ChildMenuItemStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"></Setter>
<Setter Property="ItemsSource" Value="{Binding ChildMenuItems}"></Setter>
</Style>
</Window.Resources>
为了简单起见,我还删除了一些Command绑定,但你可以根据需要重新添加它。
其他提示
根据要求,这是我的ViewModels。
ViewModelBase是由studio创建的标准版本。 MainVieModel已经足够用来创建我用来试验的测试菜单。
基本上我正在努力创建一个父/子菜单类,我可以将它们用于我们向广泛的客户群销售的一系列应用程序。我希望能够让每个客户根据他们的需求获得可定制的菜单集合以及他们购买许可证的那些菜单。
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
公共类MainViewModel:ViewModelBase
{
public MainViewModel() { MakeMenus(); }
private void MakeMenus()
{
// Makes some dummy menus to test with.
Menus = new ObservableCollection<MyMenuItem>();
ParentMenu parent;
ChildMenu child;
parent = new ParentMenu("First Level");
Menus.Add(parent);
child = new ChildMenu(parent, "second level");
parent.ChildMenuItems.Add(child);
ChildMenu child2 = new ChildMenu(child, "third level");
child2.MenuCommand = new DelegateCommand(CommandTest,
CommandCanExecute_First);
child.ChildMenuItems.Add(child2);
child = new ChildMenu(parent, "second level 2");
parent.ChildMenuItems.Add(child);
child2 = new ChildMenu(child, "third level 2");
child2.MenuCommand = new DelegateCommand(CommandTest,
CommandCanExecute_Second);
child.ChildMenuItems.Add(child2);
parent = new ParentMenu("Another First");
parent.ChildMenuItems.Add(new ChildMenu(parent, "Another Second"));
Menus.Add(parent);
OnPropertyChanged("Menus");
}
private bool ExecuteToggle { get; set; }
private void CommandTest() { ExecuteToggle = !ExecuteToggle; }
public ObservableCollection<MyMenuItem> Menus { get; private set;}
public bool CommandCanExecute_First() { return ExecuteToggle; }
public bool CommandCanExecute_Second() { return !ExecuteToggle; }
}