WPF:命令

1、理解命令

在设计良好的Windows应用程序中,应用程序逻辑不应该位于事件处理程序中,而应在更高层的方法中编写代码。其中的每个方法都代表单独的应用程序“任务”。每个任务可能依赖其他库(例如,单独编译的封装了业务逻辑或数据库访问的组件)。如下图显示了这种关系:


使用这种设计最明显的方式是在需要的地方添加事件处理程序,并使用各个事件处理程序调用恰当的应用程序方法。本质上,窗口代码变成一个精简的交换台,可以响应输入,并将请求转发到应用程序的核心。

尽管这种设计非常合理,但却没有减少任何工作。许多应用程序任务可通过各种不同的路由触发,所以经常需要编写多个事件处理程序来调用相同的应用程序方法。就其本身而言,这并不是什么问题(因为交换台代码非常简单),但当需要处理用界面的状态时,问题就变得复杂了。

下面通过一个简单的例子说明问题。设想有一个程序,该程序包含应用程序方法PrintDocument()。可以使用4中方式触发该方法:通过主菜单(选项File|Print菜单)、通过上下文菜单(在某处右击并选择Print菜单项)、通过键盘快捷键(Ctrl+P)以及工具栏按钮。在应用程序生命周期的特定时刻,需要暂时禁用PrintDocument()任务。这意味着需要禁用两个菜单命令和一个工具栏按钮,以使它们不能被单击,并且需要忽略Ctrl+P快捷键。编写代码完成这些工作,可能会使不同状态的代码块不正确地相互重叠,从而导致某个控件在不应该可用时而被启用。编写和调试这类代码是Windows开发中最为枯燥和的内容之一。

幸运的是,WPF使用新的模型帮助您解决这些问题,它增加了两个重要特性:

·将事件委托到适当的命令。

·使控件的启用状态和相应的命令的状态保持同步。

WPF命令模型不像您所期望的那样直观。为了嵌入路由事件模型,需要几个单独元素。然而,命令模型在概念上非常简单。


2、WPF命令模型

WPF命令模型,都具有如下4个重要元素:

·命令。

·命令绑定。

·命令源。

·命令目标。

2.1、ICommand接口

WPF命令模型的合兴使System.Windows.Input.ICommand接口,该接口定义了命令的工作原理。接口包含两个方法和一个事件:

public interface ICommand
{
   void Execute(object parameter);
   bool CanExecute(object parameter);
   event EventHandler CanExecuteChanged;
}
在一个简单的实现中,Execute()方法将包含应用程序的任务逻辑(例如,打印文档)。CanExecute()方法返回命令的状态--如果命令可用,就返回true;如果不可用,就返回false。Execute()和CanExecute()方法都接受一个附加的参数对象,可以使用该对象传递所需的任何附加值。

最后,当命令状态改变时引发CanExecuteChanged事件。对于使用命令的任何控件,这是指示信号,表示它们应当调用CanExecute()方法检擦命令状态。通过使用该事件,当命令可用时,命令源可自动启用自身;当命令不可用时,禁用自身。

2.2 RoutedCommand类

当创建自己的命令时,不会直接实现ICommand接口;而是使用System.Windows.Input.RoutedCommand类,该类自动实现了ICommad接口。RoutedCommand类是WPF中唯一实现了ICommand接口的类。换句话说,所有WPF命令都是RoutedCommand类及其派生类的子类实例。

在WPF命令模型背后的一个重要概念是,RoutedCommand类不包含任何应用程序逻辑,而只是代表命令,这意味着各个RoutedCommand对象具有相同的功能。

RoutedCommand类为事件冒泡和隧道添加了额外的基础结构。除了继承了ICommand接口的命令思想,可使命令在WPF元素层次结构中冒泡,以便获得正确的事件处理程序。

为支持路由事件,RoutedCommand类私有地实现了ICommand接口,并添加了ICommand接口方法的一些不同版本。Execute和CanExecute使用了一个额外的参数。下面是新的签名:

public void Execute(object parameter,IInputElement target)
{···}
public bool CanExecute(object parameter,IInputElement target)
{···}
参数target是开始处理时间的元素。事件从target元素开始,然后冒泡至高层容器,直到应用程序为了执行合适的任务而处理了事件(为了处理Execute事件,元素还需要借助于另一个类---CommandBinding类的帮助)。

除了上面的修改外,RoutedCommand类还引入了三个属性:命令名称(Name属性)、包含命令的类(OwnerType)以及任何可以用于触发命令的按键或鼠标操作(位于InputGestures集合中)。

2.3RoutedUICommand类

在程序中处理的大部分命令不是RoutedCommand对象,而是RoutedUICommand类的实例,RoutedUICommand类继承自RoutedCommand类。RoutedUICommand类用于

具有文本的命令,这些文本显示在用户界面中的某些地方(例如菜单项文本、工具栏按钮的工具提示)。RoutedUICommand类只增加了Text属性,该属性是为命令现实的文本。

为命令定义命令文本的有点是可以在某个位置执行本地化。但如果命令永远不回在用户界面的任何地方显示,那么RoutedUICommand类和RoutedCommand类是等效的。不一

非得在界面上使用RoutedUICommand,有时候命令可以用图表表示。

2.4命令库

WPF设计着认识到,每个应用程序肯呢个都有大量命令,并且对于许多不同的应用程序,很多命令是通用的。例如,所有基于文档的应用程序都有他们自己版本的New、Open

以及Save命令。为了减少创建这些命令所需的工作,WPF提供了基本命令库,基本命令库中保存的命令超过了100条。这些命令通过一下5个专门的静态类的静态属性提供:

·ApplicationCommands:该类提供了通用命令,包括剪切板命令(如Copy、Cut和Paste)以及文档命令(如New、Open、Save、Save As和Print等)。

·NavigationCommands:该类提供了用于导航的命令,包括为基于页面的应用

·EdtingCommands:该类提供了许多重要的文档编辑命令,

·ComponentCommands:该类提供了由用户界面组件使用的命令

·MediaCommands:该类提供了一组用于处理多媒体的命令。

ApplicationCommands类提供了一组命令,在所有类型的应用程序中都会经常用到这些命令,所以在此简单介绍一下。下面列出了所有这些命令。



3、执行命令

触发命令需要有命令源,响应命令需要有命令绑定,命令绑定将执行转发给普通的事件处理程序。

3.1命令源

命令库中的命令始终可用。触发它们的最简单方法是将他们关联到实现了ICommandSource接口的控件,其中包括继承自ButtonBase类的控件(Button和CheckBox等)、单独

的ListBoxItem对象和Hyperlink和MenuItem

ICommandSource接口定义了三个属性,如下表所示。


例如,下面的按钮使用Command属性链接到ApplicationCommands.New命令:

<Button Command=ApplicationCommands.New></BUtton>
3.2 命令绑定

当将命令关联到命令源时,会看到一些有趣的现象。命令源将会自动被禁用。这时因为该按钮已经查询了命令的的状态,而且由于命令还没有与其关联的绑定,所以按钮认为

是禁用的。

为了改变这种状态,需要为命令创建绑定以明确一下三件事情:

·当命令被触发时执行什么操作;

·如何确定命令是否能够被执行(这是可选的。如果未提共这一细节,只要提供了关联的时间处理程序,命令总是可用的)

·命令在何处起作用。例如,命令可被限制在单个按钮中使用,或在整个窗口中使用

下面代码片段为New命令创建绑定。可将这些代码添加到窗口的构造函数中:

 CommandBinding binding = new CommandBinding(ApplicationCommands.New);
 binding.Executed += binding_Executed;
 this.CommandBindings.Add(binding);

上面的CommandBinding对象呗包含在窗口的CommandBindings集合中,这通过冒泡进行工作。实际上当单击按钮时,CommandBinding.Executed事件从按钮冒泡到包含元素。

CommandBindings属性实际在UIElement基类中定义,者意味着任何元素都支持该属性。比如按钮。但是为了得到最大灵活性,命令banding通常被添加到顶级窗口。如果希望在多个窗口中使用相同的命令需要在这些窗口中分别创建命令绑定。

也可以后处理CommandBinding.PreviewExecuted事件,首先在最高层次的容器或窗口中引发该事件,然后隧道路由至按钮。在事件完成之前,可以通过隧道拦截和停止事件,如果将RoutedEventArgs.Handler属性设置为true,将永远不会发生Executed事件。

3.3 使用多命令源

按钮示例中触发普通事件的方式看起来不那么直接。然而,当添加使用相同命令的的更多控件时,额外命令层的意义就会体现出来。例如,可添加如下也使用New命令菜单项:

3.4 微调命令文本

即然菜单具有自动提取命令文本的功能,您可能会好奇其他ICommandSource类是否也具有类似的功能,如Button控件。可以,但需要完成一点额外的工作。

可以使用两种技术重用命令文本。一种选择是直接从静态命令对象中提取文本。

 <Button Content="{x:Static ApplicationCommands.New}" Command="ApplicationCommands.New"/>
更好的方式是通过数据绑定表达式。在此使用的数据绑定有些不寻常因为它绑定到当前元素,获取正在使用的Command对象,并提取其Text属性。下面非常负载的语法:

<Button Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" Command="ApplicationCommands.New"/>
可以通过更具有想象力的方式使用该技术。例如使用一小幅图像设置按钮的内容,而在按钮工具提示中使用数据绑定表达式显示命令名。

不能直接修改命令绑定的Text属性,该属性不是依赖属性,更改之后,不会通知界面更新文本。

3.5 直接调用命令

并非只能使用实现了ICommandSource接口的类来触发希望执行的命令。也可以用Execute()方法直接掉用来自任何事件处理程序的方法。这时需要传递参数值(或Null)和对目标元素的引用:

目标元素是WPF开始查找命令绑定的地方。可使用包含窗口(具有命令绑定)或嵌套的元素(例如,实际引发事件的元素)。也可在关联的CommandBinding对象中调用Execute()方法。在这种情况下,不需要提供目标元素,因为会将公开正在使用的CommandingBindings集合的元素设置为目标元素。









  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值