MVVM路由和中继命令

RoutedCommand和RelayCommand有什么不同? 何时使用RoutedCommand以及何时在MVVM模式中使用RelayCommand?

RoutedCommand是WPF的一部分,而RelayCommand是由WPF Disciple,Josh Smith创build的)。

但是,RS Conley认真地描述了一些不同之处。 关键区别在于RoutedCommand是一个ICommand实现,它使用RoutedEvent来路由树,直到find命令的CommandBinding,而RelayCommand不执行路由,而是直接执行一些代理。 在MV-VM场景中,RelayCommand(Prism中的DelegateCommand)可能是更好的select。

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

区别在于RelayCommand可以接受委托。 您可以在ViewModel之外定义RelayCommand。 当ViewModel创build并将命令绑​​定到UI对象(如控件)时,ViewModel可以将该委托添加到该命令中。 代表可以访问ViewModel的私有variables,因为它们是在View Model本身的范围内定义的。

它被用来减lessViewModel中包含的代码量,因为趋势是将一个Routed命令定义为ViewModel中的一个嵌套类。 两者的function在其他方面是相似的。

我认为RoutedCommands在严格的MVVM中是完全合法的。 尽pipeRelayCommands通常比较简单,但RoutedCommands有时还能提供组织优势。 例如,您可能需要几个不同的视图来连接到一个共享的ICommand实例,而不直接将该命令暴露给底层的ViewModels。

作为一个方面说明,请记住,严格的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实现命令function)需要暴露一个自定义的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),而任意数量的Views都可以通过RoutedCommand附加到它,而不需要直接绑定到视图模型。

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