本篇文章学习于: 刘铁猛老师《深入浅出WPF》
命令是什么?
你可能会问:“有了路由事件为什么还需要命令系统呢?”事件的作用是发布、传播一些消息,消息送达接收者,事件的使命也就完成了,至于如何响应事件送来的消息事件并不做规定,每个接收者可以使用自己的行为来响应事件。也就是说,事件不具有约束力。命令与事件的区别就在于命令是具有约束力的。
的确,实际编程工作中就算只使用事件、不使用命令,程序的逻辑也一样可以被驱动得很好,但我们不能阻止程序员按自己的习惯去编写代码。比如保存事件的处理器,程序员们可以写Save()/SaveHandler()/SaveDocument(),这些都符合代码规范,但迟早有一天整个项目会变得无法被读懂,新来的程序员或修改 bug 的程序员会很抓狂。
如果使用命令,情况会好很多—当Save命令到达某个组件时,命令会主动去调用组件的Save()方法,而这个方法可能被定义在基类或者接口里(即保证了这个方法一定是存在的),这就在代码结构和命名上做了约束。不但如此,命令还可控制接收者“先做校验、再保存、然后关闭”,也就是说,命令除了可以约束代码,还可以约束步骤逻辑,这让新来的程序员想犯错都难,也让修改bug 的程序员很快能找到规律、容易上手。
命令系统的基本元素和关系
WPF的命令系统由几个基本要素构成,它们是:
- 命令(Command):WPF 的命令实际上就是实现了ICommand 接口的类,平时使用最多的是RoutedCommand类。我们还会学习使用自定义命令。
- 命令源(Command Source):即命令的发送者,是实现了ICommandSource接口的类。很多界面元素都实现了这个接口,其中包括 Button、Menultem、ListBoxltem等。
- 命令目标(Command Target):即命令将发送给谁,或者说命令将作用在谁身上。命令目标必须是实现了IInputElement接口的类。
- 命令关联(Command Binding):负责把一些外围逻辑与命令关联起来,比如执行之前对命令是否可以执行进行判断、命令执行之后还有哪些后续工作等。
小试命令——使用 RoutedCommand
<Window x:Class="Demo5.Wpf命令.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo5.Wpf命令"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel x:Name="stackPanel">
<TextBox x:Name="tbTest" Background="AliceBlue" FontSize="50"/>
<Button x:Name="btnCommand" Content="发送命令"/>
</StackPanel>
</Window>
namespace Demo5.Wpf命令 {
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
InitCommand();
}
// 声明并定义命令
private RoutedCommand clrCmd = new RoutedCommand("Clear", typeof(Window));
private void InitCommand() {
// 把命令赋值给命令源,并指定快捷键 “Alt+C”
this.btnCommand.Command = clrCmd;
this.clrCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
// 指定命令目标
this.btnCommand.CommandTarget = this.tbTest;
// 创建命令关联
CommandBinding cb=new CommandBinding();
cb.Command = this.clrCmd;// 只关注与clrCmd相关的事件
cb.CanExecute += Cb_CanExecute;
cb.Executed += Cb_Executed;
// 把命令关联安置在外围控件上
this.stackPanel.CommandBindings.Add(cb);
}
// 当探测 命令是否可以执行时,此方法被调用
private void Cb_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
if(string.IsNullOrEmpty(this.tbTest.Text)) {
e.CanExecute = false;
} else {
e.CanExecute= true;
}
e.Handled = true;// 避免继续向上传而降低程序性能
}
// 当命令送达目标后,此方法被调用
private void Cb_Executed(object sender, ExecutedRoutedEventArgs e) {
this.tbTest.Clear();
e.Handled = true;
}
}
}
命令库和命令参数
微软在WPF类库里准备了一些便捷的命令库,这些命令库包括:
- ApplicationCommands
- ComponentCommands
- NavigationCommands
- MediaCommands
- EditingCommands
它们都是静态类,而命令就是用这些类的静态只读属性以单件模式暴露出来的。
前面提到命令库里有很多WPF 预制的命令,如 New、Open、Copy、Cut、Paste等。这些命令都是ApplicationCommands类的静态属性,所以它们的实例永远只有一个,
这就引出一个问题:如果界面上有两个按钮,一个用来新建Teacher 的档案,另一个用来新建Student 的档案,都使用New命令的话,程序应该如何区别新建的是什么档案呢?
答案是使用CommandPrameter。
近观命令
WPF的命令是实现了ICommand 接口的类。ICommand 接口非常简单,只包含两个方法和一个事件:
- Execute方法:命令执行,或者说命令作用于命令目标之上。需要注意的是,现实世界中的命令是不会自己“执行”的,它只能“被执行”,而在这里,执行变成了命令的方法,颇有点儿拟人化的味道。
- CanExecute方法:在执行之前用来探知命令是否可被执行。
- CanExecuteChanged事件:当命令可执行状态发生改变时,可激发此事件来通知其他对象。
RoutedCommand 就是这样一个实现了ICommand接口的类。RoutedCommand在实现ICommand接口时,并未向Execute和 CanExecute方法中添加任何逻辑,也就是说,它是通用的、与具体业务逻辑无关的。
标签:CanExecute,接口,命令,Command,事件,WPF From: https://www.cnblogs.com/swbna/p/17441691.html