电脑技术学习

了解WPF中的路由事件和命令

dn001
命令路由

路由命令与路由事件的区别在于命令从其调用程序路由至处理程序的方法。具体来说,路由事件是从幕后在命令调用程序和处理程序之间路由消息(通过将其挂接至可视树中的命令绑定)。

这 样,众多元素间都存在关联,但在任何时刻都实际只有一个命令处理程序处于活动状态。活动命令处理程序由可视树中命令调用程序和命令处理程序的位置、以及 UI 中焦点项的位置共同决定。路由事件用于调用活动命令处理程序以询问是否应启用命令,并调用命令处理程序的 Executed 方法处理程序。

通 常,命令调用程序会在自己在可视树中的位置与可视树根项之间查找命令绑定。如找到,绑定的命令处理程序会确定是否启用命令并在调用命令时一并调用其处理程 序。如果命令挂接到工具栏或菜单中的一个控件(或将设置 FocusManager.IsFocusScope = true 的容器),则会运行一些其他的逻辑,沿可视树路径从根项到命令绑定的焦点元素进行查看。

在图 3 的简单应用程序中,实际发生的情况是:由于 Cut 命令按钮位于工具栏内,所以由具备焦点项的 TextBox 实例处理 CanExecute 和 Execute。如图 3 中的文本框包含在用户控件之内,您就有机会对窗口、包含 Grid 的用户控件、包含文本框的用户控件或单个文本框设置命令绑定。有焦点项的文本框将确定其路径的终点(它的起点是根项)。

要理解 WPF 路由命令的路由,需要认识到一旦调用一个命令处理程序,就不能再调用其他处理程序。因此,如果用户控件处理 CanExecute 方法,就不会再调用 TextBox CanExecute 实现。

定义命令

ApplicationCommands.Save 和 ApplicationCommands.Cut 是 WPF 提供的诸多命令中的两个命令。图 4 中显示了 WPF 中五个内置命令类及其所包含的一些命令示例。

图4 WPF命令类

命令类示例命令

ApplicationCommands Close、Cut、Copy、Paste、Save、Print

NavigationCommands BrowseForward、BrowseBack、Zoom、Search

EditingCommands AlignXXX、MoveXXX、SelectXXX

MediaCommands Play、Pause、NextTrack、IncreaseVolume、Record、Stop

ComponentCommands MoveXXX、SelectXXX、ScrollXXX、ExtendSelectionXXX

XXX 代表操作的集合,例如 MoveNext 和 MovePrevious。每一类中的命令均定义为公用静态(在 Visual Basic? 中共享)属性,以便您可轻松挂接。通过使用以下方式,您可以轻松定义自己的自定义命令。稍后我会提供相应的示例。

您也可搭配使用一个简短的注释,如下所示:

<Button Command="Save">Save</Button>

如 您使用此缩写版本,WPF 中的类型转换器将尝试从内置命令集合找到命名的命令。在此例中结果完全相同。我倾向于使用长名版本,这样代码更为明确、更易维护。不会对命令的定义位置产 生歧义。即使是内置命令,在 EditingCommands 类和 ComponentCommands 类之间也会有一些重复。

命令插入

路由命令是 WPF 所定义的 ICommand 界面的一种特殊实现。ICommand 的定义如下:

public interface ICommand {

event EventHandler CanExecuteChanged;

bool CanExecute(object parameter);

void Execute(object parameter);

}

内置的 WPF 命令类型为 RoutedCommand 和 RoutedUICommand。这两种类均实现 ICommand 界面并使用我先前所介绍的路由事件执行路由。

我 们期望命令调用程序调用 CanExecute 来确定是否启用任何相关的命令调用代码。命令调用程序可通过订阅 CanExecuteChanged 事件来确定何时调用该方法。在 RoutedCommand 类中,根据状态或 UI 中焦点项的变化触发 CanExecuteChanged。调用命令时,会调用 Executed 方法并通过路由事件沿可视树分派至处理程序。

支持Command属性的类(如ButtonBase)实现ICommandSource界面:

public interface ICommandSource {

ICommand Command { get; }

object CommandParameter { get; }

IInputElement CommandTarget { get; }

}

Command 属性在调用程序和它将调用的命令之间建立关联。CommandParameter 允许调用程序在调用命令的同时传递某些数据。您可使用 CommandTarget 属性根据焦点项的路径替换默认路由,并通知命令系统使用指定的元素做为命令处理程序,而不是依赖路由事件和命令处理程序基于焦点项所做的决定。