路由和中继命令

路由指令接力指令的区别是什么? 什么时候使用 RoutedCommand,什么时候在 MVVM 模式下使用 RelayCommand?

53232 次浏览

区别在于 RelayCommand 可以接受委托。您可以在 ViewModel 之外定义 RelayCommand。然后,ViewModel 可以在创建命令并将该命令绑定到 UI 对象(如控件)时向该命令添加委托。委托依次可以访问 ViewModel 的私有变量,因为它们是在 View Model 本身的作用域中定义的。

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

RoutedCommand 是 WPF 的一部分,而 接力指令是由 WPF 的信徒 Josh Smith 创建的;)。

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

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

密码位置

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

RS Conley 所说的 RelayCommand 允许您在 ViewModel 之外定义 RelayCommand 是正确的,但是首先它允许您在 ViewModel 之外定义它 在里面,而 RoutedCommand 不允许。

路由

另一方面,RelayCommand 不支持通过树进行路由(如前所述) ,只要接口基于单个 viewModel,这不是问题。如果不是这样,例如,如果您有一个具有自己 viewModel 的项集合,并且希望立即为父元素中的每个项调用子 ViewModel 的命令,那么您将不得不使用路由(也参见 CompositeCommands)。

总之,我要说的是,标准的 RoutedCommand 在严格的 MVVM 中是不可用的。RelayCommand 非常适合 MVVM,但是不支持路由,您可能需要路由。

我认为 RoutedCommand 在严格的 MVVM 中是完全合法的。尽管 RelayCommand 通常因其简单性而更受欢迎,但 RoutedCommand 有时也提供了组织优势。例如,您可能希望多个不同的视图连接到共享的 ICommand 实例,而不直接将该命令公开给底层 ViewModel。

另外,请记住,严格的 MVVM 并不禁止使用代码隐藏。如果是这样,那么您就永远不能在视图中定义自定义依赖项属性!

为了在严格的 MVVM 框架内使用 RoutedCommand,您可以遵循以下步骤:

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

    public static class MyCommands {
    public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  2. Attach the desired views to the RoutedCommand using XAML:

    <Button Command="{x:Static local:MyCommands.MyCustomCommand}" />
    
  3. One of your views which is bound to a suitable ViewModel (i.e. whichever ViewModel implements the command functionality) needs to expose a custom DependencyProperty which will be bound to your ViewModel's implementation:

    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. The same view should bind itself to the RoutedCommand from step 1. In the 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. Finally, bind your ViewModel's command implementation (which should be an ICommand) to the custom dependency property in XAML:

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

The advantage of this approach is that your ViewModel only needs to provide a single implementation of the ICommand interface (and it can even be a RelayCommand), while any number of Views can attach to it via the RoutedCommand without needing to be directly bound to that ViewModel.

Unfortunately there is a downside in that the ICommand.CanExecuteChanged event will not work. When your ViewModel wants the View to refresh the CanExecute property then you must call CommandManager.InvalidateRequerySuggested().