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()方法将包含应用程序任务逻辑(例如,打印文档)。然而,正如下一节中将看到,WPF的实现更复杂。它使用Execute()方法引发一个更复杂的过程,该过程最终触发在应用程序其他地方处理的事件。通过这种方式可以使用预先准备好的命令类,并插入自己的逻辑。还可以灵活地再几个不同地方使用同一个命令(如Print命令)。
CanExecute()方法返回命令的状态——如果命令可用,就返回true;如果不可用,就返回false。Execute()和CanExecute()方法都接受一个附加的参数对象,可使用该对象传递所需的任何附加信息。
最初,当命令状态改变时引发CanExecuteChanged事件。对于使用命令的任何事件,这是指示信号,表示它们应当调用CanExecute()方法检查命令的状态。通过使用该事件,当命令可用时,命令源(如Button或MenuItem)可自动启用自身;当命令不可用时,禁用自身。
二、RoutedCommand类
当创建自己的命令时,不会直接实现ICommand接口;而是使用System.Windows.Input.RoutedCommand类,该类自动实现了ICommand接口。RoutedCommand类是WPF中唯一实现了ICommand接口的类。换句话说,所有WPF命令都是RoutedCommand类及其派生类的实例。
在WPF命令模型背后的一个重要概念是,RoutedCommand类不包含任何应用程序逻辑,而只代表命令,这意味着各个RoutedCommand对象具有相同的功能。
RoutedCommand类为事件冒泡和隧道添加了一些额外的基础结构。鉴于ICommand接口封装了命令的思想——可被触发的动作并可被启用或禁用——RoutedCommand类对命令进行了修改,使命令可在WPF元素层次结构中冒泡,以便获得正确的事件处理程序。
为支持路由事件,RoutedCommand类私有地实现了ICommand接口,并添加了ICommand接口方法的一些不同版本。最明显的变化是,Execute()和CanExecute()方法使用了一个额外参数。下面的新的签名:
public void Execute(object parameter,IInputElement target) { //... } public void CanExecute(object parameter,IInputElement target) { //... }
参数target是开始处理事件的元素。事件从target元素开始,然后冒泡至高层的容器,直到应用程序为了执行合适的任务而处理了事件(为了处理Executed事件,元素还需要借助于另一个类——CommandBinding类的帮助)。
除上面的修改外,RoutedCommand类还引入了三个属性:命令名称(Name属性)、包含命令的类(OwnerType)以及任何可用于触发命令的按键或鼠标操作(位于InputGestures集合中)。
三、RoutedUICommand类
在程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand类的实例,RoutedUICommand类继承自RoutedCommand类(实际上,WPF提供的所有预先构建好的命令都是RoutedUICommand对象)。
RoutedUICommand类用于具有文本的命令,这些文本显示在用户界面中的某些地方(例如菜单项文本、工具栏按钮的工具栏提示)。RoutedUICommand类只添加了Text属性,该属性是为命令显示的文本。
为命令定义命令文本(而不是直接在控件上定义文本)的优点是可在某个位置执行本地化。但如果命令文本永远不会在用户界面的任何地方显示,那么RoutedUICommand类和RoutedCommand类是等效的。
四、命令库
WPF设计者认识到,每个应用程序可能都有大量命令,并且对于许多不同的应用程序,很多命令时通用的,例如,所有基于文档的应用程序都有他们自己版本的New、Open以及Save命令。为减少创建这些命令所需的工作,WPF提供了基本命令库,基本命令库中保存的命令超过100条。这些命令通过以下5个专门的静态类的属性提供:
- ApplicationCommands:该类提供了通用命令,包括剪贴板命令(如Copy、Cut和Paste)以及文档命令(如New、Open、Save、SaveAs和Print等)。
- NavigationCommands:该类提供了用于导航的命令,包括为基于页面的应用程序设计的一些命令(如BrowseBack、BrowSeForward和NextPage),以及其他适合于基于文档的应用程序的命令(如IncreaseZoom和Refresh).
- EditingCommands:该类提供了许多重要的文档编辑命令,包括用于移动的命令(MoveToLineEnd、MoveLeftByWord和MoveUpByPage等),选择内容的命令(SelectToLineEnd、SelectLeftByWord),以及改变格式的命令(ToggleBold和ToggleUnderLine)。
- ComponentCommands:该类提供了由用户界面组件使用的命令,包括用于移动和选择内容的命令,这些命令和EditingCommands类中的一些命令类似(甚至完全相同)。
- MediaCommands:该类提供了一组用于处理多媒体的命令(如Play、Pause、NextTrack以及IncreaseVolume).
ApplicationCommands类提供了一组基本命令,在所有类别的应用程序中都经常会用到这些命令,所以在此简单介绍一下。下面列出了所有这些命令。
New | Copy | SelectAll |
Open | Cut | Stop |
Save | Paste | ContextMenu |
SaveAs |
Delete | CorrectionList |
Close | Undo | Properties |
Redo | Help | |
PrintPreview | Find | |
CancelPrint | Replace |
例如,ApplicationCommands.Open是提供RoutedUICommand对象的静态属性,该对象表示应用程序中的Open命令。因为ApplicationCommands.Open是静态属性,所以在整个应用程序中只有一个Open命令实例。然而,根据命令源的不同(换句话说,是在用户界面的什么地方触发的该命令),可采用不同的处理方式。
每个命令的RoutedUICommand.Text属性和名称是相互匹配的,指示在单词之间添加了空格。例如,ApplicationCommands.SelectedAll命令的文本时SelecteAll(Name属性使用相同的没有空格的文本)。因为Open命令时ApplicationCommands类的静态属性,所以RoutedUICommand.OwnerType属性返回ApplicationCommands类的类型对象。
这些单独的命令对象仅时一些标志起,不具有实际功能。然而,许多命令对象都有一个额外的特征:默认输入绑定。例如,ApplicationCommands.Open命令被映射到Ctrl+O快捷键。只要将命令绑定到命令源,并为窗口添加命令源,这个快捷键就会被激活,即使没有在用户界面的任何地方显示该命令也同样如此。