MVVM路由和继电器命令

78

什么是RoutedCommandRelayCommand之间的区别?在MVVM模式中何时使用RoutedCommand,何时使用RelayCommand?

4个回答

70

RoutedCommand 是 WPF 的一部分,而 RelayCommand 则是由 WPF 爱好者 Josh Smith 创建的。

严肃地讲,RS Conley 描述了它们之间的某些差异。关键区别在于 RoutedCommand 是一个 ICommand 实现,它使用 RoutedEvent 进行路由,直到找到该命令的 CommandBinding 为止;而 RelayCommand 不进行路由,而是直接执行某个委托。在 M-V-VM 场景中,RelayCommand(Prism 中的 DelegateCommand)可能是更好的选择。


35

关于在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,但不支持路由,这可能是您需要的。


感谢您在解释中提供了额外的深度,并提到了CompositeCommands - 这帮助我看到它们的适用范围。 - Lars Kemmann

22

不同之处在于RelayCommand可以接受代理。您可以在ViewModel之外定义RelayCommand。然后,当ViewModel创建并将命令绑定到UI对象(如控件)时,ViewModel可以添加委托到命令中。这些委托反过来可以访问ViewModel的私有变量,因为它们是在ViewModel本身的作用域中定义的。

它的使用可以减少ViewModel中包含的代码量,因为目前的趋势是将Routed命令定义为ViewModel内部的嵌套类。其他方面,这两者的功能相似。


15
我认为在严格的MVVM中使用RoutedCommands是完全合法的。尽管RelayCommands通常更简单易用,但RoutedCommands有时可以提供组织上的优势。例如,您可能希望多个不同的视图连接到共享的ICommand实例,而不直接将该命令暴露给底层的ViewModels。
顺便提一下,记住严格的MVVM并不禁止使用代码后台。如果这是真的,那么你就永远无法在你的视图中定义自定义依赖属性!
要在严格的MVVM框架内使用RoutedCommand,可以按照以下步骤操作:
1. 为您的自定义命令声明静态的RoutedCommand实例。如果您打算使用ApplicationCommands类中预定义的命令,则可以跳过此步骤。例如:
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。
    不幸的是,缺点在于ICommand.CanExecuteChanged事件将不起作用。 当您的ViewModel希望View刷新CanExecute属性时,您必须调用CommandManager.InvalidateRequerySuggested()。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接