В чем разница между RoutedCommand и RelayCommand? Когда использовать RoutedCommand и когда использовать RelayCommand в шаблоне MVVM?
Команда маршрутизации и реле 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().