Команда маршрутизации и реле MVVM

В чем разница между RoutedCommand и RelayCommand?  Когда использовать RoutedCommand и когда использовать RelayCommand в шаблоне MVVM?

Ответ 1

RoutedCommand является частью WPF, а RelayCommand был создан учеником WPF Джошем Смитом;).

Серьезно, однако, RS Конли рассказал о некоторых отличиях. Основное различие заключается в том, что RoutedCommand - это реализация ICommand, которая использует RoutedEvent для маршрутизации через дерево до тех пор, пока не будет найдено CommandBinding для команды, в то время как RelayCommand не выполняет маршрутизацию и вместо этого непосредственно выполняет какой-либо делегат. В сценарии M-V-VM RelayCommand (DelegateCommand in Prism), вероятно, лучший выбор.

Ответ 2

Что касается использования RelayCommand и RoutedCommand в MVVM, основное отличие для меня заключается в следующем:

Расположение кода

RelayCommand позволяет реализовать команду в любом классе (как ICommand-свойство с делегатами), который затем условно привязывается к элементу управления, который вызывает команду. Этот класс является ViewModel. Если вы используете маршрутизированную команду, вам придется реализовать методы, связанные с командой в коде управления элемента управления, потому что методы задаются атрибутами элемента CommandBinding. Предполагалось, что строгий MVVM означает наличие "пустого" кода-кода, на самом деле нет возможности использовать стандартные маршрутизируемые команды с MVVM.

Что RS Конли сказал, что RelayCommand позволяет вам определить RelayCommand за пределами ViewModel, это правильно, но в первую очередь это позволяет определить его внутри ViewModel, который отсутствует в RoutedCommand.

Routing

С другой стороны, RelayCommands не поддерживают маршрутизацию через дерево (как было сказано ранее), что не является проблемой, если ваш интерфейс основан на одном viewModel. Если это не так, например, если у вас есть коллекция элементов со своими собственными viewModels и вы хотите вызвать команду дочернего ViewModel для каждого элемента из родительского элемента сразу, вам придется использовать маршрутизацию (см. Также CompositeCommands).

В целом, я бы сказал, что стандартные RoutedCommands не могут использоваться в строгом MVVM. RelayCommands идеально подходят для MVVM, но не поддерживают маршрутизацию, которая вам может понадобиться.

Ответ 3

Разница в том, что RelayCommand может принимать делегаты. Вы можете определить RelayCommand вне ViewModel. Затем ViewModel может добавлять делегатов в команду, когда он создает и связывает команду с объектом пользовательского интерфейса, например с элементом управления. Делегаты, в свою очередь, могут получить доступ к частной переменной ViewModel, поскольку они определены в области самой модели просмотра.

Он используется для сокращения количества кода, содержащегося в ViewModel, поскольку тренд заключается в определении команды Routed как вложенного класса внутри ViewModel. Функциональность этих двух аналогична.

Ответ 4

Я бы сказал, что RoutedCommands совершенно законны в строгом MVVM. Хотя RelayCommands часто предпочтительнее для их простоты, RoutedCommands иногда предлагают организационные преимущества. Например, вам может потребоваться несколько разных представлений для подключения к общему экземпляру ICommand без непосредственного отображения этой команды в базовые ViewModels.

Как примечание, помните, что строгий MVVM не запрещает использование кода. Если это так, вы никогда не сможете определить свойства пользовательской зависимости в своих представлениях!

Чтобы использовать RoutedCommand в строгой структуре MVVM, вы можете выполнить следующие действия:

  • Объявить статический экземпляр RoutedCommand для вашей пользовательской команды. Этот шаг можно пропустить, если вы планируете использовать предопределенную команду из класса ApplicationCommands. Например:

    public static class MyCommands {
        public static RoutedCommand MyCustomCommand = new RoutedCommand();
    }
    
  • Прикрепите нужные представления к RoutedCommand с помощью XAML:

    <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); }
        }
    
  • Тот же вид должен привязываться к RoutedCommand с шага 1. В XAML:

    <UserControl.CommandBindings>
        <CommandBinding Command="{x:Static local:MyCommands.MyCustomCommand}"
                        CanExecute="MyCustomCommand_CanExecute"
                        Executed="MyCustomCommand_Executed"
                        />
    </UserControl.CommandBindings>
    

    В коде для вашего просмотра связанные обработчики событий просто делегируют ICommand из свойства зависимостей, объявленного на шаге 3:

    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);
        }
    }
    
  • Наконец, привяжите реализацию команды ViewModel (которая должна быть ICommand) к свойству пользовательской зависимости в XAML:

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

Преимущество такого подхода заключается в том, что ваш ViewModel должен обеспечить единую реализацию интерфейса ICommand (и это может быть даже RelayCommand), в то время как любое количество Views может присоединяться к нему через RoutedCommand, не требуя прямого доступа связанный с этой ViewModel.

К сожалению, есть недостаток в том, что событие ICommand.CanExecuteChanged не будет работать. Когда ваш ViewModel хочет, чтобы View обновил свойство CanExecute, вы должны вызвать CommandManager.InvalidateRequerySposed().