命令概述(翻译)
命令是WPF的一种输入机制,在比设备输入更高的语意级别上提供了输入操作。在许多的应用操作上可以看到Copy, Cut, Paste操作命令的例子。
本概述定义了什么是WPF的定义,哪些类是命令模型的一部分,并且在你的应用程序中怎样使用和创建命令。
这个话题包含了如下几个部分:
什么是命令
WPF的简单命令
WPF命令的四个主要概念
命令库
创建自定义命令
什么是命令?
命令有几个目的。第一个目的是为了分离从逻辑上调用命令并且执行命令的语意和对象。这就允许了多个不同的源去调用同一个命令逻辑,并且允许命令逻辑根据不同的目标进行自定义。例如,在很多应用里的编辑操作Copy, Cut, Paste,如果通过命令执行的话,可以使用不同的用户行为调用。一个应用程序可以通过点击鼠标,或者选择菜单项,或者使用组合键(CTRL+X)来使用户剪切选择的对象或者文字。通过使用命令,你可以绑定用户操作的每个类型到相同的逻辑。
命令的另一个目的是表明操作是否可执行。继续对象或文本剪切的例子,该操作仅当有些东西选择了之后才有意义。当一个用户尝试剪切一个对象和文本但没有选择任何东西时,什么事都不会发生。为了向用户表明这个,许多应用程序禁用按钮和菜单选项,所以用户知道是否可以执行一项操作。表明操作是否可以执行是通过CanExecute方法实现的。按钮可以订阅CanExecuteChanged事件,如果CanExecute返回false时是禁用的,如果CanExecute返回true时是可用的。
命令的语意可以跨过应用程序和类保持一致,但是对于特定的对象行为的逻辑是特定的。键盘组合键(CTRL+X)调用调用text类,image类和Web浏览器类的Cut命令,但是Cut操作表现的实际逻辑是通过应用程序定义来实现cut的。一个RoutedCommand使得客户端完成了逻辑。一个text对象剪切选择的文字到剪贴板,一个图片对象将会剪切选择的图片。当一个应用程序处理的Executed事件,它就获得了命令目标并且根据目标类型采取适当的操作。
WPF简单命令例子
在WPF里使用命令最简单的办法就是利用命令库里预定义的RoutedCommand,使用一个支持处理命令的控件,使用一个控件支持触发命令。Paste命令是ApplicationCommands类里预定义的命令之一。Textbox控件有内置的逻辑来操作Paste命令。并且MenuItem类支持触发命令。
如下的例子显示了怎样设置MenuItem,当他被点击的时候触发Paste命令到TextBox上,假设TextBox有键盘焦点。
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;
// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
WPF命令里的四个主要概念
WPF里的路由命令模型可以分解为四个主要概念:命令,命令源,命令目标,命令绑定:
- 命令是一种被执行的行为
- 命令源是一个触发命令的对象
- 命令目标是一个命令可以在上面执行的对象
- 命令绑定是一个对象,将命令逻辑映射到命令
在原来的例子里,Paste命令是一个命令,MenuItem是一个命令源,TextBox是一个命令目标,命令绑定由TextBox控件提供。值得注意的是,并不总是这样,命令目标类控件提供CommandBinding。经常遇到的是CommandBinding需要应用程序开发者自己提供,或者命令绑定可能附加到命令目标的祖先。
Commands
WPF里的命令是通过 ICommand接口实现的。ICommand 暴露了两个方法:Execute和CanExecute,和一个事件CanExecuteChanged.Execute来实现命令相关的行为。CanExecute决定了命令在当前的命令目标上是否可以执行。CanExecuteChanged被抛出,当命令管理器集中了命令操作检测到了命令源的改变,可能无效的命令被触发,但是命令绑定还没有执行。WPF信号ICommand 和RoutedCommand类是本概述的重点。
WPF的主要输入源氏鼠标,键盘,墨水以及路由命令。设备导向的输入是使用路由事件通知应用程序页的对象,一个输入事件发生了。路由事件也是一样的。Execute 和 CanExecute方法并没有包含命令的应用逻辑,仅仅是以冒泡或者隧道的行式抛出路由事件,通过元素树直到遇到了CommandBinding的对象。CommandBinding包含了用于这些事件的处理器,并且处理器表现了命令。
ExeCute方法是RoutedCommand在命令目标上抛出PreviewExecuted和Executed事件,CanExecute方法是RoutedCommand在命令目标上抛出CanExecute和 PreviewCanExecute事件。这些事件以隧道或者冒泡的方式通过元素树直到他们遇到了具有特定方法的CommandBinding对象。
WPF提供了一系列公共路由命令遍布几个类:MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands 和EditingCommands。这些类由RoutedCommand对象和命令未完成的逻辑组成。完成的逻辑负责命令在哪个对象上执行。
Command Sources
命令源是一个调用命令的对象。命令源的例子如:MenuItem, Button, KeyGesture.
WPF的命令源逐渐实现了ICommandSource接口。
ICommandSource暴露了三个属性:Command, CommandTarget, CommandParameter:
Command是当命令源触发的时候执行的命令
CommandTarget是执行命令的对象。值得一提的是WPF里的ICommandSource的CommandTarget属性仅当ICommand是 RoutedCommand时才是可用的。如果CommandTarget设置为ICommandSource,并且相应的命令不是RoutedCommand,命令目标可以忽略。如果没有设置CommandTarget,键盘焦点的元素将是命令目标。
Commandparameter是一个用户自定义的数据类型,用于将数据传递到操作程序实现命令。
WPF实现了ICommandSource 的类是ButtonBase, MenuItem, Hyperlink。当它们被点击的时候将会触发命令。当InputGesture与他关联的时候,一个InputBinding触发一个命令。
如下的例子显示了怎样使用ContextMenu里Menuitem作为命令源。
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Properties" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();
// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);
// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;
典型的,一个命令源将会监听CanExecuteChanged事件。这个事件通知命令源,在当前的命令目标上执行命令的能力改变了。这个命令源可以通过CanExecute方法查询当前RoutedCommand的状态。如果命令不能执行,命令源将会被禁止。例如当不能执行时,菜单栏变成灰色。
一个InputGesture可以用作命令源。两种类型的WPF输入手势是KeyGesture和MouseGesture。你可以将KeyGesture当作键盘快捷键,例如CTRL+C,一个KeyGesture由一个键和一组ModifierKeys组成。一个MouseGesture由MouseAction和一组可选的ModifierKeys组成。
如下的例子显示了怎样在KeyGesture和RoutedCommand之间创建KeyBinding。
<Window.InputBindings>
<KeyBinding Key="B"
Modifiers="Control"
Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
Key.B,
ModifierKeys.Control);
KeyBinding OpenCmdKeybinding = new KeyBinding(
ApplicationCommands.Open,
OpenKeyGesture);
this.InputBindings.Add(OpenCmdKeybinding);
另一种方法将InputGesture关联到RoutedCommand的是在RoutedCommand上将InputGesture添加到InputGestureCollection。如下的例子显示了如何将KeyGesture添加到RoutedCommand的InputGestureCollection上。
KeyGesture OpenCmdKeyGesture = new KeyGesrure(
Key.B,
ModifierKeys.Control);
ApplicationCommands.Open.InputGestures.Add(OpenCmdGesture);
CommandBinding
CommandBinding利用事件处理器来关联命令,以实现命令。
CommandBinding类包含了CommandBinding属性,和PreviewExecuted, Executed, PreviewCanExecute, CanExecute事件。
Command是一个CommandBinding相关联的命令。附加在PreviewExecuted和Executed事件上的事件处理器实现了命令逻辑。附加在PreviewExecuted和Executed事件上的事件处理器决定了命令在当前的命令目标上是否可以执行。
如下的例子显示了怎样在Windows应用程序里创建CommandBinding。CommandBinding利用Executed和CanExecute与Open命令相关联。
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Open"
Executed="OpenCmdExecuted"
CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
ApplicationCommands.Open,
OpenCmdExecuted,
OpenCmdCanExecute);
this.CommandBindings.Add(OpenCmdBinding);
接下来,创建ExecuteRoutedEventHandler和CanExecuteRoutedEventhandler。ExecuteRoutedEventHandler 打开一个MessageBox显示一个字符串表示这条命令已经执行了。CanExecuteRoutedEventhandler设置 CanExecute为true。
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
String command, targetobj;
command = ((RoutedCommand)e.Command).Name;
targetobj = ((FrameworkElement)target).Name;
MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj);
}
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
CommandBinding附加到指定的对象,例如Window根的应用程序和控件。CommandBinding对象附加到定义绑定的范围。例如,一个CommandBinding附加到命令目标的祖先,Executed事件可以到达,但是CommandBinding附加到命令目标的子孙,则不可以达到。这是一个直接方式的结果,一个RoutedEvent隧道式或者冒泡式的从一个对象抛出事件。
在一些环境下,CommandBing附加到命令绑定它自己,例如TextBox类和Cut, Copy, Paste命令。
很多时候,将CommandBinding附加到命令目标的祖先是很方便的,例如主窗口或者应用对象,特别是同样的命令绑定可以用于多个绑定目标。这些设计决定了当你创建命令架构时,你将想要考虑。
Command Target
命令目标是一个命令将被执行的元素。与RoutedCommand命令有关,这些命令目标是Executed和CanExecute开始路由的元素。正如前面所注意的,WPF里ICommandSource的CommandTarget属性仅能应用在ICommand是RoutedCommand。如果CommandTarget设置成ICommandSource,而相应的命令不是RoutedCommand,那么命令目标将被忽略。
命令源可以显示的设置为命令目标。如果命令目标没有定义,有键盘焦点的元素将被用来作为命令目标。使用具有键盘焦点的元素作为命令目标由一个好处是它允许了应用程序开发者使用相同的命令源去触发多个目标的同一个命令,而不需要跟踪命令目标。例如,如果MenuItem触发了应用程序里的Paste命令,该应用程序含有一个TextBox控件和一个PasswordBox控件,目标既可以是TextBox,也可以是PasswordBox,依赖于哪个控件有键盘焦点。
如下的例子显示了怎样在标记语言和后台代码里显示设置命令目标。
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste"
CommandTarget="{Binding ElementName=mainTextBox}" />
</Menu>
<TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;
// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
The CommandManager
CommandManager提供了许多命令相关的函数。它提供了一系列静态的方法从一个指定的元素,用来增加和删除PreviewExecuted,Executed,PreviewCanExecute和CanExecute 事件处理器。它提供了一个方法到指定的类去注册CommandBinding和InputBinding对象。CommandManager同样提供了一种方法,通过RequerySuggested事件,当它需要抛出CanExecutechanged事件时去通知命令。
InvalidReuerySuggested方法强制CommandManager抛出RequerySuggested事件。这对于启用/禁用命令的条件是非常有用的,但是没有条件时,CommandManager是知道的。
Command Library
WPF提供了一系列预定义的命令,命令库由如下的类组成:ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands 和ComponentCommands。这些类提供了命令,例如Cut, Browseback, BrowseForward, Play, Stop, Pause.
许多命令包含了一系列的输入绑定。例如,如果你规定你的的应用程序处理copy命令,你自动的活得到了键盘绑定“CTRL+C”,你同样绑定到了其他的输入设备,例如平板电脑的gesture和语音信息。
当你使用XAML在各种命令库里引用命令,你通常可以省略库的类名,暴露静态命令属性的类。通常,命令名作为字符串是模糊的,存在的所属类型提供了一个命令逻辑组,但是没有必要去混淆。例如,你可以规定Command=”Cut”, 而不用更详细的规定Command = “ApplicationCommands.Cut”。这是一个很方便的机制,内置到WPF XAML的命令处理器(更准确的说,是一种类型的转换器行为,WPF XAML处理器在负载是引用)。
Creating Custom Commands
如果命令库里的类不能满足你的命令需求,你可以自己创建自己的命令。这里有两种方法创建自定义的命令。第一种是从0开始,实现ICommand 借口。 另外一种方法,更普遍的方法,创建一个RoutedCommand或者一个RoutedUICommand。
引用:http://msdn.microsoft.com/en-us/library/ms752308(VS.100).aspx