首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.4  命令

WPF提供了内建的命令支持,这是一个更为抽象且松耦合的事件版本。尽管事件是与某个用户动作(如点击一个Button或者选中一个ListBoxItem)相关联的,但命令表示的是那些与用户界面分离的动作,最标准的命令示例是剪切(Cut)、复制(Copy)和粘贴(Paste)。应用程序总能通过许多同步的机制提供这些动作:Menu(菜单)控件中的MenuItem(菜单项)、ContextMenu(上下文菜单)控件中的MenuItem、ToolBar(工具栏)控件中的Button(按钮)、键盘快捷方式等。

可以通过事件很好地处理多个命令(如剪切、复制和粘贴)。例如,可以为3个动作分别定义一个通用的事件处理程序,然后把每个处理程序添加到相关元素上的适当事件(如Button的Click事件、主窗口的KeyDown事件等)。另外,当有对应的动作失效时,很可能需要启用或者禁止适当的控件(例如,当剪切板中没有任何东西时,禁止用户界面中的粘贴操作)。这种双向的交流方式会使问题变得复杂,特别是当你不想硬编码一些需要更新的控件的时候。

幸运的是,WPF的命令正好对症下药。这一功能减少了代码数量(在某些情况下,可以不需要任何过程式代码),这样就可以更加灵活地改变你的用户界面了,而不需要破坏后台的逻辑。但命令并不是WPF的新发明,一些较早的技术如微软基础类库(Microsoft Foundation Classes,MFC)也有类似的机制。当然,即使你很熟悉MFC,WPF的命令仍然有它们自己独特的优点需要你去学习。

大多数命令的能力来自于下面3种特性:

l    WPF定义了许多内建命令。

l    命令自动支持输入手势(input gesture),如键盘快捷方式。

l    有些WPF控件有一些与不同命令关联的内建行为。

3.4.1  内建命令

命令是任何一个实现了ICommand接口(位于System.Windows.Input命名空间)的对象,每个对象定义了3个简单的成员:

l    Execute——执行特定命令的逻辑的方法。

l    CanExecute——如果命令允许被执行,则该方法返回true;如果不允许执行,则返回false。

l    CanExecuteChanged——无论何时,只要CanExecute的值改变,该事件就会触发。

如果需要创建剪切、复制和粘贴命令,可以定义3个实现ICommand接口的类,找一个地方存储这3个类(或许可以放在主窗口的静态成员中),从相关的事件处理程序中调用Execute(当CanExecutre返回true时),处理CanExecuteChanged事件,改变相关用户界面中的IsEnabled属性。然而,这听上去并不比只使用事件好多少。

幸好像Button、CheckBox和MenuItem这样的控件有相关的逻辑会与任何命令做交互。它们会有一个简单的Command属性(类型为ICommand),当设置了Command属性后,无论何时Click事件触发,这些控件会自动调用命令的Execute方法(只要CanExecute返回true时)。另外,它们会自动保持IsEnabled的值与CanExecute的值同步,这是通过CanExecuteChanged事件实现的。通过这种给属性赋值的方式,任何逻辑在XAML下都是可以实现的。

更加幸运的是,WPF甚至已经定义了一系列命令,因此不需要为Cut、Copy和Past命令实现ICommand对象,也不用担心在哪里保存这些命令。WPF有5个类的静态属性实现了WPF的内建命令:

l    ApplicationCommands——Close、Copy、Cut、Delete、Find、Help、New、Open、Paste、Print、PrintPreview、Properties、Redo、Replace、Save、SaveAs、SelectAll、Stop、Undo等。

l    ComponentCommands——MoveDown、MoveLeft、MoveRight、MoveUp、ScrollByLine、ScrollPageDown、ScrollPageLeft、ScrollPageRight、ScrollPageUp、SelectToEnd、SelectToHome、SelectToPageDown、SelectToPageUp等。

l    MediaCommands——ChannelDown、ChannelUp、DecreaseVolume、FastForward、IncreaseVolume、MuteVolume、NextTrack、Pause、Play、PreviousTrack、Record、Rewind、Select、Stop等。

l    NavigationCommands——BrowseBack、BrowseForward、BrowseHome、BrowseStop、Favorites、FirstPage、GoToPage、LastPage、NextPage、PreviousPage、Refresh、Search、Zoom等。

l    EditingCommands——AlignCenter、AlignJustify、AlignLeft、AlignRight、Correct- SpellingError、DecreaseFontSize、DecreaseIndentation、EnterLineBreak、EnterParag- raphBreak、IgnoreSpellingError、IncreaseFontSize、IncreaseIndentation、MoveDown- ByLine、MoveDownByPage、MoveDownByParagraph、MoveLeftByCharacter、MoveLeftByWord、MoveRightByCharacter、MoveRightByWord等。

每个属性并不会返回实现ICommand的独特类型,相反,它们都是RoutedUICommand的实例。RoutedUICommand类不仅实现了ICommand接口,还可以像路由事件一样支持冒泡。

About对话框有一个Help Button,目前它什么都没做,这样就让我们在Help命令中添加一些逻辑代码,演示一下这些内嵌命令如何工作,其中的Help命令是在ApplicationCommands中定义的。假设这个按钮叫作helpButton,可以用下面的C#代码把Help命令与之关联起来:

所有的RoutedUICommand对象定义了一个Text属性,其中包含了命令的名称,适合显示在用户界面中。(该属性是RoutedUICommand和它的基类RoutedCommand唯一不同的地方。)例如,Help命令的Text属性是(不要感到惊讶)Help字符串。在这个Button上对Content做的硬编码可以用下面的代码替代:

如果你本来想在这次更改后运行About对话框,会发现这个Button现在被永远禁用了。这是因为内嵌命令不可能知道在什么时候应该启用或禁用,也不知道应该执行哪个动作,它们把这些逻辑委托给命令的使用者。

要插入自定义逻辑,需要向元素或父元素添加一个CommandBinding对象,它就会执行该命令(这多亏了有路由命令的冒泡行为)。所有派生自UIElement(和ContentElement)的类包含了一个CommandBindings集合,其中有一个或多个CommandBinding对象。因此,可以向About对话框的根元素Window中添加一个Help的CommandBinding对象,下面是它的代码隐藏文件:

提示    Text字符串是由每个RoutedUICommand定义的,WPF会自动对Text作本地化,并把它转换为任何一种WPF支持的语言。这意味着,如果一个按钮的Content属性被设置为ApplicationCommands.Help.Text,那么如果当前线程的UI文化是西班牙文(Spanish)而不是英文(English),它将自动显示“Ayuda”而不是“Help”。尽管在某些上下文中需要提供的是图像而不是文字(可能是在一个工具栏中),仍然可以在其他地方使用该本地化字符串,例如在ToolTip(工具栏提示)中。当然,你还是需要对你自己的字符串做本地化,这些字符串将在你的用户界面中显示。调整命令的Text属性可以截断你需要翻译的术语的数量。

这里假设HelpExecuted和HelpCanExecute方法已经定义。这些方法将在适当的时候被回调,这样可以插入Help命令的CanExecute和Execute方法的实现。

代码清单3-11和代码清单3-12最后一次改变About对话框,其中完全使用XAML将Help Button与Help命令绑定(虽然两个处理程序必须在代码隐藏文件中定义)。

代码清单3-11  支持Help命令的About对话框

代码清单3-12  代码清单3-11对应的代码隐藏文件

Window的CommandBinding属性之所以能在XAML中设置,是因为它定义了一个默认的构造函数,允许通过属性设置它的数据。Button的Content也可以在XAML中设置,可以设置它为选中的命令的Text属性,这多亏了有第9章中所讲到的时尚的数据绑定技术。另外,类型转换器简化了XAMl中指定Help命令的过程。CommandConverter类能够识别所有的内建命令,因此在这两处可以设置Command属性为Help,而不用使用更冗长的表达式{x:Static ApplicationCommands.Help}(但无法对自定义命令做这种特殊处理。)在代码隐藏文件中,HelpCanExecute一直把那个命令保持在启用(enabled)状态,而HelpExecuted启动了一个Web浏览器,并加载了一个适当的帮助URL。

3.4.2  使用输入手势执行命令

在这样一个简单的对话框中使用Help命令,可能就像使用一个简单的Click事件处理程序,但是它还有额外的好处(不仅仅是本地化文本):它会自动绑定到一个键盘快捷方式上。

当用户按下F1键时,应用程序会调用帮助。当代码清单3-10中定义的对话框显示在屏幕上时,如果你按下F1键,Help命令会自动被加载,就像按Help Button一样!这是因为像Help这样的命令会定义一个默认的输入手势(input gesture),该手势会执行这个命令。当把KeyBinding和MouseBinding对象添加到相关元素的InputBindings集合中时,就可以把自己的输入手势绑定到一个命令上。例如,如果要设定F2作为执行Help命令的键盘快捷方式,需要在AboutDialog构造函数中添加下面语句:

 

然而,这样的话F1和F2都会执行Help命令。如果把F1绑定到一个特殊的NotACommand命令上,可以改变F1的默认行为,如下所示:

这两句语句可以用下面的XAML语句替代:

3.4.3  带有内建命令绑定的控件

似乎有些神奇,但是WPF中的一些控件确有自己的命令绑定。最简单的例子就是TextBox控件,它有自己的Cut、Copy和Paste命令的内建绑定,这些命令可以与剪贴板交互,还有Undo和Redo命令的内建绑定。这不仅仅意味着TextBox会响应标准的Ctrl+X、Ctrl+C、Ctrl+V、Ctrl+Z、Ctrl+Y键盘快捷方式,其他元素能很容易地参与到这些动作中来。

下面的XAML展示了这些内建命令绑定的力量:

可以把这些内容放到XamlPad中,或者保存为一个.xaml文件,然后在IE中浏览,因为这里不需要任何程序代码。这5个按钮每一个都与一个命令相关联,它们会把Content属性设置为每个命令的Text属性中的字符串。这里唯一新的就是每个Button的CommandTarget属性设置,该属性指向那个TextBox实例(这里仍然是通过数据绑定功能完成的,这将在第9章中讲解)。这会使命令从TextBox被执行,而不是从Button那被执行,其实这很有必要,因为这样它才能对命令做出响应。

这段XAML生成的结果在图3-8中。如果TextBox中没有文本被选中,前两个Button将被自动禁用;而当有文本被选中时,这两个按钮将被自动启用。类似地,当剪贴板中有文本内容的时候,粘贴Button会自动被启用;如果没有,就会自动被禁用。

图3-8  5个按钮在没有程序代码的情况下仍然可以运行自如,这多亏了TextBox的内建绑定功能

虽然通过命令,Button和TextBox可以实现很多的交互,但它们互相并不了解。这也可以说明为什么WPF的内建命令是如此重要。随着第三方用于规范WPF内建命令的控件越来越多,控件之间的交互将越来越趋于无缝,虽然这些控件彼此不了解。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论