Яка різниця між RoutedCommand та RelayCommand ? Коли використовувати RoutedCommand, а коли RelayCommand у шаблоні MVVM?
Яка різниця між RoutedCommand та RelayCommand ? Коли використовувати RoutedCommand, а коли RelayCommand у шаблоні MVVM?
Відповіді:
RoutedCommand є частиною WPF, тоді як RelayCommand був створений учнем WPF Джошем Смітом;).
Серйозно, однак, Р. С. Конлі описав деякі відмінності. Ключова відмінність полягає в тому, що RoutedCommand - це реалізація ICommand, яка використовує RoutedEvent для маршрутизації по дереву, поки не буде знайдено CommandBinding для команди, тоді як RelayCommand не виконує маршрутизацію, а натомість безпосередньо виконує якийсь делегат. У сценарії MV-VM RelayCommand (DelegateCommand in Prism), мабуть, кращий вибір навколо.
Щодо використання RelayCommand та RoutedCommand у MVVM, то головна відмінність для мене полягає в наступному:
Розташування коду
RelayCommand дозволяє вам реалізувати команду в будь-якому класі (як властивість ICommand з делегатами), яка потім прив'язується до даних елементу управління, який викликає команду. Цей клас - ViewModel . Якщо ви використовуєте маршрутизовану команду, вам доведеться реалізувати методи, пов'язані з командою, у коді позаду елемента управління, оскільки методи задаються атрибутами елемента CommandBinding. Припускаючи, що суворий MVVM означає наявність "порожнього" коду позаду файлу, насправді немає можливості використовувати стандартні маршрутизовані команди з MVVM.
Те, що сказав Р. С. Конлі, говорить, що RelayCommand дозволяє визначити RelayCommand поза ViewModel правильно, але перш за все це дозволяє визначити його всередині ViewModel, чого RoutedCommand не робить.
Маршрутизація
З іншого боку, RelayCommands не підтримують маршрутизацію по дереву (як вже було сказано раніше), що не є проблемою, якщо ваш інтерфейс базується на одному viewModel. Якщо це не так, наприклад, якщо у вас є колекція елементів з їх власними viewModels і ви хочете викликати команду дочірнього ViewModel для кожного елемента з батьківського елемента одночасно, вам доведеться використовувати маршрутизацію (див. Також CompositeCommands) .
Загалом, я б сказав, що стандартні команди RoutedCommands не можна використовувати в суворих MVVM. RelayCommands ідеально підходять для MVVM, але не підтримують маршрутизацію, яка вам може знадобитися.
Різниця полягає в тому, що RelayCommand може приймати делегатів. Ви можете визначити команду RelayCommand поза ViewModel. Потім ViewModel може додавати делегатів до команди, коли вона створює та прив'язує команду до об'єкта користувальницького інтерфейсу, як елемент керування. Делегати, в свою чергу, можуть отримати доступ до приватної змінної ViewModel, як вони визначені в межах самої моделі перегляду.
Він використовується для зменшення кількості коду, що міститься у ViewModel, оскільки тенденція полягає у визначенні команди Routed як вкладеного класу всередині ViewModel. Функціональність обох в іншому випадку схожа.
Я стверджую, що 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), тоді як до нього через RoutedCommand може приєднуватися будь-яка кількість переглядів без необхідності безпосереднього зв’язку з цим ViewModel.
На жаль, є недолік у тому, що подія ICommand.CanExecuteChanged не буде працювати. Коли ваш ViewModel хоче, щоб подання оновило властивість CanExecute, тоді ви повинні викликати CommandManager.InvalidateRequerySugnged ().