特别说明:本节非手动翻译,翻译时,本人使用的工具如下:台式机 *2,笔记本电脑 *1,小刀 *1,十字螺丝刀 *1,一字螺丝刀 *1,笔记本硬盘 *2,启动U盘 *1,移动硬盘 *1,Final Recovery *1,Easy Recovery *1,电子书 *1。翻译过程自行想像,反正用了这些东西以后,Command这一节就翻译出来了,至于后面的那一节,乱码太多了。。。。
于是,本人对本节翻译质量完全不做保证,不排除偶尔出现几个乱码(虽然本人校对了一下似乎没有),也不保证全文没有任何串掉的地方。。反正我看着还是满通顺的。
本节说明了MVVM模式中如何使用Command
除了提供View中的数据访问和数据修改以外,View Model很有可能会定义其它供用户使用的业务或者行为。在WPF和Silverlight中,用户可以在UI中操作的业务或者行为通常被定义为Command。Command方便的表示可以绑定到UI中的控件上的业务和行为。它封装了实现该业务或者行为的实际代码并且使其于实际界面脱耦。
Command可以认为是由用户与View交互的各种途径而调用或者表示。在大多数情况下,它都是以鼠标点击的形式被调用,但是它们也可以由点击快捷键,鼠标手势,或者其它输入事件所调用。在View中的控件可以通过绑定View Model中的Command以使它们可以响应无论是点击或者是其它输入事件。Command与View中的用户控件的交互是双向的。在本例中,Command可以被UI中的控件所调用,UI中的控件也可以根据底层Command的启用状态自动启用或者禁用。
View Model实现Command可以以Command Method的形式或者以Command Object (一个实现ICommand接口的对象)的形式。无论哪种形式,与View交互的Command都不需要在后置代码中声明任何事件响应代码。比如,某些WPF或者Silverlight控件本身就支持Command并且也提供了Command属性用以绑定View Model中的ICommand对象。对于其它控件,则通过Command Behavior用以连接控件与View Model中的Command Method或者是Command。
【注意】:Behavior是强大并且灵活的体系,它封装了可以被View中控件绑定的交互逻辑和行为。
以下的章节中描写了如何实现View中的Command,以Command Method的形式或者以Command Object的形式,以及如何将它与View中的控件结合到一起。
Command Object是一个实现ICommand接口的对象。这个接口定义了封装操作本身的Execute 方法,以及指示方法在特定时刻是否可以执行的CanExecute 方法,这些方法都只有一个参数。对封装到Command Object中的方法和逻辑作单元测试或者维护都会更加方便。
在使用的时候,可以直接实现ICommand接口。也可以通过使用一些实现该接口的类来简化实现过程。比如,可以使用由Expression Blend SDK提供的ActionCommand类,也可以使用由Prism提供的DelegateCommand。
由Prism提供的DelegateCommand封装了代表View Model类中的两个方法的委托。它继承于DelegateCommandBase类,该类通过调用委托实现了ICommand接口的Execute和CanExecute方法。可以使用如下所示的构造函数为委托指定在View Model中的方法。
关联视图与由ViewModel提供的命令对象的方法有许多。在某些WPF或者Silverlight 4控件中,由其是继承于ButtonBase的控件(比如Button,RadioButton,Hyperlink,MenuItem及它们的子类),都可以通过Command属性轻松绑定命令对象。WPF也支持将ViewModel中的命令绑定到KeyGesture上。
<Button Command="{Binding Path=SubmitCommand}" CommandParameter="SubmitOrder"/>
命令的参数也可以由CommandParameter属性任意定义,参数的类型是由指定命令的Execute和CanExecute函数所指定的。控件会自行在用户与它发生交互时调用命令,并且如果有提供参数,那么参数将被传递到Execute函数中去。比如在上一个例子中,按钮在被点击时会调用SubmitCommand方法。另外,如果有指定CanExecute函数,那么根据该函数的返回值按钮会自动的切换可用状态,在返回false按钮被禁用,反之启用。
也可以使用Expression Blend Interaction Triggers和InvokeCommandAction行为来完成绑定。
<Button Content="Submit" IsEnabled="{Binding CanSubmit}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding SubmitCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
这种方法可以用以任何可以附加交互触发器的控件。这种方法在希望将命令附加到一些不继承与ButtonBase的控件上时由其好用,或者在需要控件响应除了点击事件以外的事件。同样的,如果需要传参那么就使用CommandParameter属性。
但是与直接绑定不同的是,InvokeCommandAction无法通过CanExecute函数自动更新控件的可用属性。如果需要实现这一行为,那么可以手动将控件的IsEnabled属性绑定到ViewModel中的某个属性上,如前例所述。
【注意】:Command-Enabled控件vs. Behaviors
WPF和Silverlight4中那些支持Command的控件都可以将控件与相应的命令以定义的方式钩在一起。这些控件都会在用户与之互动时调用指定的命令。比如说,一个Button控件,命令会在用户点击按钮的时候调用。这种关联是固定的,不可变的。
而Behaviors同样也提供了以定义的方法关联控件与相应的命令。但是Behaviors可以关联被控件响应的一组事件,并且可以根据要求关联命令对象或者直接关联ViewModel中的Command Method。换而言之,Behaviors可以适用于大多数据Command-Enable控件可以使用的场景,并且它会提供更多的灵性性和可控性。
使用时,需要根据调用行为的种类来判断到底使用何种方法。如果需要统一的将控件与ViewModel中的某个方法相关联,或者需要保证整体一致性,那么就考虑使用Behaviors,即使是被绑定的控件支持Command也是如此。
如果只需要使用Command-Enabled控件去调用ViewModel里的命令,或者控件提供的响应命令的默认事件能够满足需求,那么就没必要使用Behaviors。另外,如果开发者或者UI设计师本身就不使用Expression Blend,那么使用Command-Enabled控件(或者自行附加命令)会更好。因为那些附加的语句都需要使用Expression Blend行为。
另一种以ICommand方式实现命令的方法仅需要在ViewModel中实现一个方法并且在View中直接通过Behavior调用。
这种方法和前一节中演示过的使用Behavior调用命令相似。但是使用CallMethodAction取代掉原来的InvokeCommandAction。以下代码就是如何调用由ViewModel提供的(无参的)Submit方法
<Button Content="Submit" IsEnabled="{Binding CanSubmit}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding}" Method="Submit"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
TargetObject则是通过{Binding}表达式绑定到底层的数据上下文(也就是ViewModel),Method则是指定调用哪个方法。
【注意】:CallMethodAction不支持参数;如果需要向目标方法传参,那么就需要在ViewModel中提供相应属性,或者使用InvokeCommandAction,又或者直接重写一个支持参数的CallMethodAction。