理解命令
传统的设计在需要地方添加事件处理程序,并使用各个事件处理程序调用恰当的应用程序方法。如下图,许多应用程序任务可通过各种不同的路由触发,所以经常编写多个事件处理程序来调用相同的应用程序方法。本身这并不是什么问题。但当需要处理用户界面状态时,问题就变复杂了。如果要禁用PrintDocumnt()任务。需要禁用两个菜单命令和一个工具栏按钮,使它们不能被单击并且禁用Ctrl+P快捷键。编写代码完成这些工作很麻烦。
WPF使用新的命令模型解决这个问题。它增加了两个重要特性。
将事件委托到适当的命令。
使控件的启用状态和相应命令的状态保持同步。
WPF命令模型
WPF命令模型具有如下4个重要元素
命令:命令表示应用程序任务,并且跟踪任务是否能被执行。然而,命令实际上不包含执行应用程序任务的代码。
命令绑定:每个命令绑定针对用户界面的具体区域,将命令连接到相关的应用程序逻辑,这种分解的设计是非常重要的,因为每个命令可用于应用程序的多个地方, 并且每个地方具有不同的意义。为处理这一问题,需要同一命令与不同的命令绑定。
命令源:命令源触发命令.例如:MenuItem和Button都是命令源。单击他们会执行绑定命令。
命令目标:命令目标是在其中执行命令的元素。例如,Paste命令可在TextBox控件中插入文本,而OpenFile命令可在DocumentViewer中打开文档。
ICommand接口
WPF命令模型的核心是System.Windows.Input.ICommand接口。
public interface ICommand { void Execute(object parameter); bool CanExecute(object parameter); event EventHandler CanExecuteChanged; }
Execute()方法将包含应用程序任务逻辑。
CanExecute()方法返回命令的状态。
通过CanExecuteChanged事件,当命令可用时,命令源可自动启用自身,当命令不可用时,禁用自身。
RoutedCommand类
RoutedCommand类是WPF中唯一实现了ICommand接口的类。所有WPF命令都是RoutedCommand类及其派生类的实例。
RoutedUICommand类
RoutedUICommand类继承自RoutedCommand类,WPF提供的所有预先构建好的命令都是RoutedUICommand对象。
命令库
WPF提供了基本命令库,基本命令库中保存的命令超过100条,这些命令通过以下5个专门的静态类的静态属性控制。
ApplicationCommands:该类提供了通用命令。包括剪切板命令(如Copy、Cut、Paste)以及文档命令(如New、Open、Save、Save As和Print)
NavigationCommands:该类提供了用于导航的命令,包括基于页面的应用程序设计的一些命令,如BrowseBack、BrowseForward和NextPage。
EditingCommands:该类提供了许多重要的文档编辑命令。包含用于移动的命令,MoveToLineEnd、MoveLeftByWord和MoveUpByPage,选择内容命令SelectToLineEnd、SelectLeftByWord。
ComponentCommands:改类提供了由用户界面组件使用的命令,包括用于移动和选择内容的命令,这些命令和EditingCommand类中的一些命令类似。
MediaCommands:该类提供了一组用于处理多媒体的命令,如Play、Pause、NextTrack以及IncreaseVolume。
命令源
命令库中的命令始终可用。触发他们最简单的方法是将他们关联到实现了ICommandSource接口的控件,其中包括继承自ButtonBase类的控件,Button和CheckBox、ListBoxItem、HypeLink以及MenuItem。
ICommandSource接口定义了三个属性
Command 指向连接的指令。
CommandParameter 提供其他希望随命令发送的数据。
CommandTarget确定将在其中执行命令的元素。
使用Command属性连接到ApplicationCommands.New
<Button Command="ApplicationCommands.New">New</Button>
命令绑定
当命令关联到命令源时,命令源将会被自动禁用。如上按钮,查询了命令的状态,而且由于命令还没有与其关联的绑定,所以按钮是禁用的。
代码创建绑定
CommandBinding binding = new CommandBinding(ApplicationCommands.New); binding.Executed+=binding_Executed;this.CommandBindings.Add(binding); xaml <Window.CommandBindings> <CommandBinding Command="ApplicationCommands.New" Executed="binding_Executed"></CommandBinding> </Window.CommandBindings> <StackPanel> <Button Command="ApplicationCommands.New">New</Button> </StackPanel>
使用多命令源
<Menu> <MenuItem Header="File"> <MenuItem Command="New"></MenuItem> </MenuItem> </Menu>
微调命令文本
菜单可以自动提取命令项文本。button 要实现此功能。
<Button Command="New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}"></Button>
直接调用命令
ApplicationCommands.New.Execute(null, targetElement); this.CommandBindings[0].Command.Execute(null);
禁用命令
需求文本变时,保存按钮才可用
public GridDemoWindow() { InitializeComponent(); CommandBinding binding = new CommandBinding(ApplicationCommands.Save); binding.Executed+=binding_Executed; binding.CanExecute+=binding_CanExecute; this.CommandBindings.Add(binding); } private void binding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = isDirty; } private void binding_Executed(object sender, ExecutedRoutedEventArgs e) { throw new NotImplementedException(); } private void txt_TextChanged(object sender,RoutedEventArgs e) { isDirty = true; }
自定义命令
public class DataCommands { private static RoutedUICommand requery; static DataCommands() { InputGestureCollection inputs = new InputGestureCollection(); inputs.Add(new KeyGesture(Key.R, ModifierKeys.Control, "Ctrl+R")); requery = new RoutedUICommand("Requery","Requery",typeof(DataCommands),inputs); } public static RoutedUICommand Requery { get { return requery; } } }
引用命名空间
xmlns:local="clr-namespace:Commands"
使用命令
<Window.CommandBindings> <CommandBinding Command="local:DataCommands.Requery" Executed="binding_Executed"></CommandBinding> </Window.CommandBindings> <StackPanel> <Button Command="local:DataCommands.Requery">Requery</Button> </StackPanel>