WPF中的命令与命令绑定(二)
WPF中的命令与命令绑定(二)
周银辉
在WPF中,命令(Commanding)被分割成了四个部分,分别是ICommand,ICommandSource,CommandTarget和CommandBinding。下面我们来分别探讨这四个部分。
1,ICommand
Command也就是我们的“命令”本身,比如“复制”“粘贴”。在WPF中,所有的命令都必须实现ICommand接口,它为所有的命令提供一个抽象,这个抽象对于我们实现Undo、Redo操作非常重要,如果你学习一下设计模式中的“命令”模式,你会更加深刻的理解。
ICommand接口中拥有Execute()方法,该方法用于命令的执行(不过,注意:命令的执行逻辑——比如将剪切板中的文本去出来放到文本框的合适位置——并没有被编写到该方法中,稍后我们会讲到这其中的奥妙),另外的一个方法是CanExecute()用于指示当前命令在目标元素上是否可用,当这种可用性发生改变时其便会引发该接口的尾页一个事件CanExecuteChanged。
在目前的WPF类库中,你能看到唯一一个实现了ICommand接口的类型RoutedCommand(其实还有一个名为SecureUICommand的类也实现了该接口,不过该类未被公开),“Routed”是一个不太容易被翻译的修饰词(有人将它翻译为“路由”),但这意味着该类型的命令可以向WPF中的RoutedEvent一样在元素树中上下传递。
RoutedCommand的子类RoutedUICommand是我们经常使用的类型,它与RoutedCommand的不同之处仅仅在与它多了一个Text属性来描述该命令,不过大多数WPF内置命令的Text属性有一个很不错的特点:其支持自动本地化。这至少会为我们的软件的本地化减少工作量。
在本系列随笔的后续部分将介绍如何自定义一个命令。
2,ICommandSource与CommandTarget
命令源,用来触发我们的命令,比如用一个菜单项来触发“复制”命令,那么该菜单项就是命令源。要使一个元素成为命令源,其必须实现ICommandSource接口。命令源决定了它所要触发的命令、该命令所作用的对象以及命令参数(如果需要的话),这分别对应于它的三个属性:Command、CommandTarget以及CommandParameter。其中需要注意的是CommandTarget,因为在WPF中如果你不为命令源指定其命令对象,那么其将会把界面上获得键盘焦点的元素作为默认的命令对象,这为我们提供了方便,比如界面上有两个文本框,我们不必担心主菜单项上的“粘贴”操作是针对哪个文本框的,谁获得焦点便针对谁,这符合大家的习惯。但引入的问题是,如果命令目标不具备获取键盘焦点的能力(比如Label)或命令源会抢占焦点(比如用Button来代替菜单项,点击按钮时焦点由文本框转移到了按钮上),你的命令将会无效,这时你就必须为命令源指定命令目标。
在本系列随笔的后续部分将介绍如何让你的自定义控件成为命令源和命令目标。
3,CommandBinding
前面已经提到我们并没有将命令的执行逻辑编写到其Excute()方法中,这是有道理的,比如"粘贴"命令(ApplicationCommands.Paste),粘贴一段文本到文本框和粘贴一个图片到绘图板的执行逻辑肯定是不一样的,负责开发该“粘贴”命令的开发人员不可能知道所有的粘贴操作的具体逻辑,使用“粘贴”命令的客户也不应该为该执行逻辑负责,编写该执行逻辑的任务应该被分发给那些支持“粘贴”操作的控件的开发人员以及那些希望为自己的控件添加“粘贴”操作的客户。也就是说我们需要将“行为的请求者(命令)”和“行为的执行者(命令的执行逻辑)”分开而实现一种松耦合,而CommandBinding(命令绑定)便是命令和命令执行逻辑的桥接器。
我们使用CommandBinding将命令与其合适的执行逻辑绑定在一起:
ApplicationCommands.Close, CloseCommandHandler, CanExecuteHandler);
CommandBinding构造方法的最后两个参数分别是ExecutedRoutedEventHandler 与 CanExecuteRoutedEventHandler 类型的委托,用于指示如何执行命令和如何判断命令能否被执行。
与CommandBinding一样扮演着中间角色的还有CommandManager类,它为命令绑定(以及输入绑定)提供了很多实用方法。
在本系列随笔的后续部分将介绍WPF的命令系统与“命令模式”(设计模式之一)之间的关系。