В чём отличие команд от обработчиков событий?

В WPF и MVVM существует четкая парадигма использовать команды, наследующие ICommand, отделяющие логику от представления и т.д. Однако, после перехода с событийной модели на MVVM не совсем понятно, в чём их отличия от обработчиков событий?


Ответы (2 шт):

Автор решения: alladuh

Думаю, что основное преимущество команд - это разделение логики и представления (то, что вы сказали). Немного уточнений:

  • Вы создаёте одну команду и можете использовать её во многих View, много раз. Событие же тесно связано с источником, поэтому обработчик вешается на ту View, на котором висит это событие;
  • Это ведёт к тому, что команды проще тестировать;
  • У команд есть два метода: Execute и CanExecute. Очень удобно то, что в CanExecute можно определить условие, когда команда сработае, а когда нет.

Также можно почитать вот эти две ссылки (правда они на английском):

1) https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-3.5/ms752308(v=vs.90)?redirectedfrom=MSDN

2) https://stackoverflow.com/questions/8452/custom-wpf-command-pattern-example

В дополнении можно добавить, что в wpf реализовано много встроенных команд. Тут есть их список: https://metanit.com/sharp/wpf/7.1.php

→ Ссылка
Автор решения: aepot

Попробую своими словами, так как сам недавно слез с WinForms-подхода на MVVM и рад этому. Мой дилетантский взгляд, на гениальность не претендую.

События

  • Обработчик события получает ссылку на контрол в sender и все необходимые данные о событии в EventArgs. Далее можно делать с ними что требуется: менять поведение контрола, или слать данные куда подальше. Набор входных данных фиксирован и не кастомизируется.
  • Событие вызывается тогда, когда нам надо, нажата клавиша, наведена мышь, и так далее. Есть много разных событий, которые могут подойти практически под любой вид задач.

Но

  • Находятся все в одном классе
  • Сложно тестировать

Команды

  • Находятся где угодно, куда можно ссылаться при объявлении Binding, по умолчанию там, куда ссылается DataContext.
  • В команду можно передать любой аргумент.
  • Команду можно вызвать когда надо через прямое подключение, например к кнопке, или через KeyBindings.
  • Разрешено ли выполнять команду, узнать можно до начала ее выполнения, условие помещается в CanExecute. При этом многие контролы имеют встроенную реакцию на CanExecute, и например сами могут переводить свое состояние в Disabled, если CanExecute вернет false (например MenuItem или Button). Это удобно.
  • Легко тестировать просто вызвав команду отдельно из тестового метода.

Команды при этом не всегда нужны, например они не нужны, если мы хотим что-то сделать, если у контрола меняется какое-то свойство. В этом случае мы просто можем забиндить само свойство и выполнять код по факту изменения свойства. Так же команды не нужны, чтобы менять внешний вид или поведение контрола. В 99% случаев, это решается прямо в xaml с помощью стилей и шаблонов.

На текущий момент недостатков у команд я не вижу, просто их назначение совсем другое, не такое как у обработчиков событий, я бы даже сравнивать не стал.

Наиболее удобный способ использования команд, на мой взгляд, лучше всего описан здесь в разделе Relaying Command Logic.

→ Ссылка