在Windows Presentation Foundation (WPF)中,创建一个动态菜单是一项常见的任务,它允许开发者根据程序的运行状态动态地添加、修改或删除菜单项。本文将分三部分介绍如何实现这一功能。首先,将实现ICommand接口,然后设置菜单的基础结构,并在最后将所有部分整合在一起。
在WPF中,ICommand接口允许定义一个命令,当点击菜单项时执行,并且可以指定是否可以执行该命令。WPF使用这个接口来决定菜单项是否应该被启用。以下是ICommand接口的一个简单实现:
public class MenuCommand : ICommand
{
private Action execute;
private Func canExecute;
public MenuCommand(Action execute, Func canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public void Execute(object parameter)
{
execute();
}
public bool CanExecute(object parameter)
{
return this.canExecute();
}
private void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
}
在初始化ICommand时,传递一个要执行的动作(Action)和一个返回布尔值的函数(Func)。在第一部分中提到的MenuItem类有一个默认返回true的虚拟方法。如果需要禁用菜单项,可以覆盖这个方法,但大多数情况下不需要这样做。
CanExecuteChanged事件与命令管理器的RequerySuggested事件关联,这个事件在用户界面中触发得足够频繁,使得禁用操作看起来像是实时发生的。
要使用层次化数据模板,需要做两件事:将菜单栏绑定到顶级父项,然后将数据模板绑定到菜单项的子项列表。层次化数据模板将遍历所有菜单项并获取子项,直到构建出完整的树形结构。在每种情况下,还绑定菜单项对象的文本属性以显示给用户。RecognizesAccessKey属性允许使用下划线(_)指定快捷键。
还需要创建一个样式,绑定每个MenuItem的OnSelected命令,并将该样式应用到每个MenuItem上。
<Window x:Class="DynamicMenuExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" SizeToContent="Manual">
<Window.Resources>
<Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding OnSelected}"/>
</Style>
</Window.Resources>
<Grid>
<DockPanel LastChildFill="True">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Menu IsMainMenu="True" ItemsSource="{Binding ParentItems}" Width="525">
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}" ItemContainerStyle="{StaticResource MenuItemStyle}">
<ContentPresenter Content="{Binding Text}" RecognizesAccessKey="True"/>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
</StackPanel>
</DockPanel>
</Grid>
</Window>