77

RoutedCommandRelayCommand 有什么区别?在 MVVM 模式中何时使用 RoutedCommand 以及何时使用 RelayCommand?

4

4 回答 4

69

RoutedCommand是 WPF 的一部分,而RelayCommand是由 WPF 门徒 Josh Smith 创建的;)。

不过,说真的,RS Conley 描述了其中的一些差异。主要区别在于 RoutedCommand 是一个 ICommand 实现,它使用 RoutedEvent 在树中路由,直到找到该命令的 CommandBinding,而 RelayCommand 不进行路由,而是直接执行一些委托。在 MV-VM 场景中,RelayCommand(Prism 中的 DelegateCommand)可能是更好的选择。

于 2009-03-19T20:34:47.943 回答
35

关于在 MVVM 中使用 RelayCommand 和 RoutedCommand,对我来说主要区别如下:

代码位置

RelayCommand 允许您在任何类中实现命令(作为具有委托的 ICommand 属性),然后通常将数据绑定到调用命令的控件。这个类是ViewModel。如果使用路由命令,则必须在控件的代码隐藏中实现与命令相关的方法,因为这些方法由 CommandBinding 元素的属性指定。假设严格的 MVVM 意味着有一个“空”的代码隐藏文件,实际上不可能将标准路由命令与 MVVM 一起使用。

RS Conley 所说的,RelayCommand 允许您在 ViewModel 之外定义 RelayCommand 是对的,但首先它允许您在 ViewModel内部定义它,而 RoutedCommand 没有。

路由

另一方面,RelayCommands 不支持通过树进行路由(如前所述),这不是问题,只要您的界面基于单个 viewModel。如果不是,例如,如果您有一组具有自己的 viewModel 的项目,并且想要一次从父元素中为每个项目调用子 ViewModel 的命令,则必须使用路由(另请参见 CompositeCommands) .

总而言之,我想说,标准的 RoutedCommands 在严格的 MVVM 中不可用。RelayCommands 非常适合 MVVM,但不支持您可能需要的路由。

于 2011-06-22T14:21:24.417 回答
22

不同之处在于 RelayCommand 可以接受委托。您可以在 ViewModel 之外定义 RelayCommand。然后,ViewModel 可以在创建命令并将命令绑​​定到 UI 对象(如控件)时向命令添加委托。委托反过来可以访问 ViewModel 的私有变量,因为它们是在 View Model 本身的范围内定义的。

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

于 2009-03-16T12:53:47.967 回答
15

我认为 RoutedCommands 在严格的 MVVM 中是完全合法的。尽管 RelayCommands 通常因其简单性而受到青睐,但 RoutedCommands 有时会提供组织优势。例如,您可能希望多个不同的视图连接到一个共享的 ICommand 实例,而无需将该命令直接暴露给底层的 ViewModel。

作为旁注,请记住严格的 MVVM 并不禁止使用代码隐藏。如果那是真的,那么您将永远无法在视图中定义自定义依赖项属性!

为了在严格的 MVVM 框架中使用 RoutedCommand,您可以按照以下步骤操作:

  1. 为您的自定义命令声明一个静态 RoutedCommand 实例。如果您打算使用 ApplicationCommands 类中的预定义命令,则可以跳过此步骤。例如:

    public static class MyCommands {
        public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  2. 使用 XAML 将所需的视图附加到 RoutedCommand:

    <Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
    
  3. 您绑定到合适 ViewModel 的视图之一(即,无论哪个 ViewModel 实现命令功能)都需要公开一个自定义 DependencyProperty,该自定义 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); }
        }
    
  4. 相同的视图应将自身绑定到步骤 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);
        }
    }
    
  5. 最后,将 ViewModel 的命令实现(应该是 ICommand)绑定到 XAML 中的自定义依赖项属性:

    <local:MainView DataContext="{Binding MainViewModel}"
                    MyCustomCommand="{Binding CustomCommand}" />
    

这种方法的优点是您的 ViewModel 只需要提供 ICommand 接口的单个​​实现(它甚至可以是 RelayCommand),而任意数量的视图可以通过 RoutedCommand 附加到它,而无需直接绑定到那个视图模型。

不幸的是,有一个缺点是 ICommand.CanExecuteChanged 事件将不起作用。当您的 ViewModel 希望 View 刷新 CanExecute 属性时,您必须调用 CommandManager.InvalidateRequerySuggested()。

于 2012-07-17T19:30:22.270 回答