1. Command 的简述
这一节可看可不看, 类似传销。
1.1. 其作用
简而言之,Command就是响应用户 键盘快捷键输入或者控件事件 (如button点击, 工具条,菜单栏等等),
从而完成如复制、黏贴、打印等操作的一个过程。
1.2. 为什么选择Command
如果你之前是开发MFC界面程序, 并第一次接触WPF, 你肯定会说, 那不就是消息处理吗。就算是WPF
中,事件(牛人们都说事件是消息的封装,我还没搞过原理, 所以不能解释更多)不也能轻松搞定这些工作吗。
你的理解是对的, 至少和我的理解是一样的, 事件能搞定Command想要做的所有工作。但是如果你知道
了使用Command的好处的话, 你就会觉的 那我们还是多用Command吧。
我举几个说明好处的例子,Command都是实现了ICommand的类的对象, 如RoutedCommand、RoutedUICommand类. 那么这些类必须实现
其中的三个成员: 一个事件CanExecuteChanged, 两个函数CanExecute及Execute.
- Execute是用于执行具体的逻辑, 比如具体怎么打印(创建一个打印框实例等等)。
- CanExecuteChange 和 CanExecute是控制 发出命令的控件的状态,如Undo工具按钮,如果你什么都没有编辑过的话,这个按钮就应该是无法使用的状态。如果你要控制这个状态, 代码非常简单, 只需要将CanExecute函数返回false (按钮无法使用)或true(按钮可用)就可以了。
并且还有一个非常大的好处, 就是实现一个Command就可以直接关联到多个发命令的控件, 如一个Command可以同时关联菜单栏中的打印按钮, 工具条中的打印按钮以及快捷键。这些只需要简单的关联操作就可以了, 从而使你不必在Execute或CanExecute中做 任何判断命令来源的逻辑。如果你开发过MFC, 如果是不同的消息源发出的命令消息, 处理的时候必须对消息的来源进行判断, 然后进行具体的业务逻辑处理。
Command的好处就是上面的两点, 一个是Execute和CanExecute函数只需处理业务逻辑, 不需要处理控件状态、 判断命令来源等繁琐的事情;一个是可以通过非常简单的操作关联多个命令源。 那么如果是使用事件来实现呢? 你可以想到就没有这两个好处了, 很多情都需要你自己做了。
1.3. 一个命令有那几部分组成
这段完全是引用自《深入浅出WPF》这本书的。其实只要记住了这几个组成部分,再看些源代码,就至少能初步理解命令了(我现在就是这个水平)。
1) 命令(Command):WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的是RoutedCommand类。
2) 命令源(Command Source):即命令的发送者,是实现了ICommandSource接口的类。很多界面元素都实现了这个接口,包括Button、MenuItem、ListBoxItem等。
3) 命令目标(Command Target):即命令将发送给谁,或者说命令将作用在谁身上。命令目标必须是实现了IInputElement接口的类。
4) 命令关联(Command Binding):负责把一些外围逻辑与命令关联起来,比如执行之前对命令是否可以执行进行判断、命令执行之后还有哪些后续工作等。
2. 三类 Command
在我看来命令一共有三类,
- 一类就是WPF已经定义好了的, 我们只需要直接拿来用就可以了,如ApplicationCommands, EditingCommands等等类中定义了很多复制,黏贴,打印等等命令。这些命令都是RoutedUICommand。
- 一类是自定义的,直接创建RoutedCommand或RoutedUICommand的实例。
- 还有一类也是自定义的, 自己实现ICommand接口。
NOTE:
RoutedUICommand是继承自RoutedCommand的, 多一个Text 属性, 这个属性在菜单项MenuItem中能自动显示为MenuItem中的显示文本。 如打印菜单项,如果关联了ApplicationCommands.Print命令的话, 会自动出现 "打印" 文本。简言之RoutedUICommand就是不需要自己动手实现给控件设置文本, 却没有实际业务逻辑的事情了。
2. 三类Command的介绍,实现及使用
2.1. 基础命令
2.1.1. 介绍
基础命令, 这是我的叫法, 有其他叫法的人忽略就可以了。
基础命令指的是WPF已经给我们定义好了的命令, 类似已经定义好了的事件(MouseDown)。
主要分为以下几类:
- ApplicationCommands 主要是一些 大家常用的命令, 如打印,复制, 剪贴, Undo, Redo, 查找等等。
- ComponentCommands 主要是滚动页面, 选择内容等命令。
- EditingCommands 主要是编辑文本或富文本的命令, 如改变字体,文本对齐, 移动光标等等。
- MediaCommands 主要是播放相关的命令,开始播放, 暂停, 停止等等。
- NavigationCommands 主要是类似浏览器中的上页 下页, 暂停刷新, 放大缩小等等。
2.1.2 例子
这些已经定义的命令就不需要我们再定义或实现了, 直接拿来用就可以了。
所以请看下面一个简单的使用例:
- 先来看下Xaml的代码
<StackPanel>
<!--这可以理解成命令的监听器,
* 当监听到这个命令的时候会执行Executed事件处理函数,
* CanExcute是命令源控件显示时就会执行的函数, 如果命令源一直处于可见状态,
消息就会持续出发,即函数持续执行-->
<StackPanel.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Print}" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"></CommandBinding>
</StackPanel.CommandBindings>
<Menu HorizontalAlignment="Left" Height="25" Margin="189,107,0,0" VerticalAlignment="Top" Width="75">
<MenuItem Header="菜单" >
<!--命令源 绑定打印命令, 点击命令源时 出发该命令-->
<!--命令目标好像实际的作用比较小, 实际上点击命令源之后,真正是通过调用RaiseEvent出发
一个消息传递的, 所以在CanExecute或E