什么是RoutedCommand和RelayCommand之间的区别?在MVVM模式中何时使用RoutedCommand,何时使用RelayCommand?
RoutedCommand 是 WPF 的一部分,而 RelayCommand 则是由 WPF 爱好者 Josh Smith 创建的。
严肃地讲,RS Conley 描述了它们之间的某些差异。关键区别在于 RoutedCommand 是一个 ICommand 实现,它使用 RoutedEvent 进行路由,直到找到该命令的 CommandBinding 为止;而 RelayCommand 不进行路由,而是直接执行某个委托。在 M-V-VM 场景中,RelayCommand(Prism 中的 DelegateCommand)可能是更好的选择。
关于在MVVM中使用RelayCommand和RoutedCommand,我认为主要区别如下:
代码位置
RelayCommand允许你在任何一个类中实现命令(作为ICommand属性带有委托),然后将其习惯性地数据绑定到调用命令的控件上。该类就是ViewModel。如果你使用路由命令,则必须在控件的codebehind中实现与命令相关的方法,因为这些方法是由CommandBinding元素的属性指定的。严格的MVVM意味着具有“空”的codebehind文件,因此实际上没有可能在MVVM中使用标准的路由命令。
RS Conley所说的RelayCommand允许您在ViewModel之外定义RelayCommand的说法没错,但首先它允许您在ViewModel内部定义它,而RoutedCommand却不行。
路由
另一方面,RelayCommands不支持沿着树进行路由(如前所述),这并不是问题,只要您的界面基于单个viewModel。如果不是这样,例如如果您有一个包含其自己的viewModels集合的项,并且想要一次从父元素调用每个子ViewModel的命令,则必须使用路由(请参见CompositeCommands)。
总的来说,我会说标准的RoutedCommands在严格的MVVM中不可用。RelayCommands非常适合MVVM,但不支持路由,这可能是您需要的。
不同之处在于RelayCommand可以接受代理。您可以在ViewModel之外定义RelayCommand。然后,当ViewModel创建并将命令绑定到UI对象(如控件)时,ViewModel可以添加委托到命令中。这些委托反过来可以访问ViewModel的私有变量,因为它们是在ViewModel本身的作用域中定义的。
它的使用可以减少ViewModel中包含的代码量,因为目前的趋势是将Routed命令定义为ViewModel内部的嵌套类。其他方面,这两者的功能相似。
public static class MyCommands {
public static RoutedCommand MyCustomCommand = new RoutedCommand();
}
使用XAML将所需的视图附加到RoutedCommand:
<Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
你的某个视图需要绑定到一个适当的 ViewModel(即实现命令功能的任何 ViewModel),该视图需要公开一个自定义 DependencyProperty,该属性将绑定到你的 ViewModel 的实现:
public partial class MainView : UserControl
{
public static readonly DependencyProperty MyCustomCommandProperty =
DependencyProperty.Register("MyCustomCommand",
typeof(ICommand), typeof(MainView), new UIPropertyMetadata(null));
public ICommand MyCustomCommand {
get { return (ICommand)GetValue(MyCustomCommandProperty); }
set { SetValue(MyCustomCommandProperty, value); }
}
同样的视图应该绑定到步骤1中的RoutedCommand。 在XAML中:
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static local:MyCommands.MyCustomCommand}"
CanExecute="MyCustomCommand_CanExecute"
Executed="MyCustomCommand_Executed"
/>
</UserControl.CommandBindings>
在您视图的代码后台中,相关的事件处理程序将仅委托给步骤3中声明的依赖属性中的ICommand:
private void MyCustomCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e) {
var command = this.MyCustomCommand;
if (command != null) {
e.Handled = true;
e.CanExecute = command.CanExecute(e.Parameter);
}
}
private void MyCustomCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
var command = this.MyCustomCommand;
if (command != null) {
e.Handled = true;
command.Execute(e.Parameter);
}
}
最后,在XAML中将您的ViewModel命令实现(应为ICommand)绑定到自定义依赖属性:
<local:MainView DataContext="{Binding MainViewModel}"
MyCustomCommand="{Binding CustomCommand}" />
这种方法的优点是,您的ViewModel只需要提供一个ICommand接口的实现(甚至可以是RelayCommand),而任何数量的视图都可以通过RoutedCommand附加到它上面,而无需直接绑定到该ViewModel。