WPF的命令系统是wpf中新增加的内容,在以往的winfom中并没有。为什么要增加命令这一块内容。在winform里面的没有命令只使用事件的话也可以实现程序员希望实现的功能。这个问题在很多文章中都提到了。但大家都是引用深入浅出wpf里面的概述。没有用自己的话来阐述。当然你仔细理解一下的话也很容易理解。但在这里我还是用我自己的话来说一下什么情况下使用命令。
命令具有约束力
事件的作用是发布、传播一些消息,消息传达到了接收者,事件的指令也就算完成了,至于如何响应事件送来的消息事件并不做任何限制,每个接收者可已用自己的行为来响应事件。也就是说,事件不具有约束力。命令和事件的区别就在于命令具有约束力。
事件是一种很自由散漫的调用。就像我们平时的自由职业者一样,不受约束,想做什么就做什么。仔细运用的话虽然可以什么功能都实现,但是总是有些凌乱。但是命令不同,命令具有约束力,也就是上班族一样。有一个制度来约束你。必须按照流程来实现功能。所说这样做略显麻烦,但是对于一些复杂特定的情况却能够避免程序员出错。
比如保存事件的处理器,程序员可以写Save()、SaveHandle()、SaveDocument()... 这些都符合代码规范。但迟早有一天整个项目会变的让人无法读懂,新来的程序员或修改bug的程序员会很抓狂。如果使用命令,情况就会好很多----当Save命令到达某个组件的时候,命令会自动去调用组件的Save方法。而这个方法可能定义在基类或者接口里(即保证了这个方法是一定存在的),这就在代码结构和命名上做了约束。
这里所谓的约束力就是指命令系统的几大要素
- 命令(Command):WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的就是RoutedCommand类。我们还会学习使用自定义命令。
- 命令源(Command Source):即命令的发送者,是实现了ICommandSource接口的类。很多界面元素都实现了这个接口,其中包括Button,ListBoxItem,MenuItem等。
- 命令目标(Command Target):即命令发送给谁,或者说命令作用在谁的身上。命令目标必须是实现了IInputElement接口的类。
- 命令关联(Command Binding):负责把一些外围逻辑和命令关联起来,比如执行之前对命令是否可以执行进行判断、命令执行之后还有哪些后续工作等。
要想使用命令,就必须要实现这几大要素。而程序员如果实现了这几大要素,那么程序的逻辑就很明了。也就是让程序员想犯错都难。
命令最常见的使用情况是例如工具栏里面绑定命令,右键命令,菜单绑定命令,保存,撤销,打开文件夹等情境。
命令实现例1
命令的实现可以自定义命令,但对于一些简单的情况我们可以使用wpf内置的已经实现了ICommand接口的RoutedCommand或者RoutedUICommand
注意RoutedUICommand只是比RoutedCommand多了一个说明性的文本参数。
xaml
<Window x:Class="WpfApplication1.Window28" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window28" Height="300" Width="300" WindowStyle="ToolWindow"> <StackPanel Background="LightBlue" x:Name="sp1"> <Button Content="Send Command" x:Name="btn1" Margin="5"></Button> <TextBox x:Name="txtA" Margin="5,0" Height="200"></TextBox> </StackPanel> </Window>
后台cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfApplication1 { /// <summary> /// Window28.xaml 的交互逻辑 /// </summary> public partial class Window28 : Window { public Window28() { InitializeComponent(); InitializeCommand(); } //声明并定义命令 private RoutedCommand rouutedCommand = new RoutedCommand("Clear",typeof(Window28)); private void InitializeCommand() { //把命令赋值给命令源,并定义快捷键 this.btn1.Command = rouutedCommand; //定义快捷键 gesture 手势 this.rouutedCommand.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt)); //指定命令目标 this.btn1.CommandTarget = txtA; //创建命令关联 CommandBinding commandBinding = new CommandBinding(); //把命令rouutedCommand和具体的逻辑事件绑定起来。 commandBinding.Command = rouutedCommand;//只关注与rouutedCommand相关的命令 commandBinding.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute); commandBinding.Executed += new ExecutedRoutedEventHandler(cb_Execute); //把命令关联安置在外围控件上 this.sp1.CommandBindings.Add(commandBinding); } //当命令到达目标之后,此方法被调用 private void cb_Execute(object sender, ExecutedRoutedEventArgs e) { txtA.Clear(); //避免事件继续向上传递而降低程序性能 e.Handled = true; } //当探测命令是否可执行的时候该方法会被调用 private void cb_CanExecute(object sender,CanExecuteRoutedEventArgs e) { if (string.IsNullOrEmpty(txtA.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } //避免事件继续向上传递而降低程序性能 e.Handled = true; } } }