Что такое Command и как её использовать?

В Xamarin.Forms Command представляет собой реализацию интерфейса ICommand и служит для связывания действий (методов ViewModel) с элементами пользовательского интерфейса, такими как Button, MenuItem, ToolbarItem и другие. Это ключевой элемент MVVM-паттерна, позволяющий обрабатывать пользовательские действия (например, нажатие кнопки) без использования обработчиков событий в code-behind, сохраняя таким образом чистую архитектуру и отделение логики от представления.

Основы ICommand

Интерфейс ICommand определяет:

public interface ICommand
{
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
  • Execute — метод, выполняющий команду.

  • CanExecute — определяет, может ли команда быть выполнена в текущий момент.

  • CanExecuteChanged — событие, вызываемое при изменении состояния доступности команды.

Использование Command в Xamarin.Forms

В Xamarin.Forms предусмотрена готовая реализация Command, которую можно использовать напрямую в ViewModel.

Простая команда без параметра

ViewModel:

public class MainViewModel : INotifyPropertyChanged
{
public ICommand ClickCommand { get; }
public MainViewModel()
{
ClickCommand = new Command(OnButtonClick);
}
private void OnButtonClick()
{
// Логика при нажатии кнопки
}
public event PropertyChangedEventHandler PropertyChanged;
}

View (XAML):

<Button Text="Нажми меня" Command="{Binding ClickCommand}" />

Команда с параметром

Команды могут принимать параметры, которые передаются через свойство CommandParameter.

ViewModel:

public ICommand ShowAlertCommand { get; }
public MainViewModel()
{
ShowAlertCommand = new Command<string>(ShowAlert);
}
private void ShowAlert(string message)
{
// Показать сообщение
}

View (XAML):

<Button Text="Показать"
Command="{Binding ShowAlertCommand}"
CommandParameter="Привет, мир!" />

Использование CanExecute для управления доступностью

Команда может быть активной или неактивной в зависимости от условий. Это контролируется логикой внутри CanExecute.

ViewModel:

private bool \_isEnabled;
public bool IsEnabled
{
get => \_isEnabled;
set
{
\_isEnabled = value;
OnPropertyChanged(nameof(IsEnabled));
((Command)SubmitCommand).ChangeCanExecute();
}
}
public ICommand SubmitCommand { get; }
public MainViewModel()
{
SubmitCommand = new Command(OnSubmit, () => IsEnabled);
}
private void OnSubmit()
{
// Логика отправки
}

View (XAML):

<Button Text="Отправить"
Command="{Binding SubmitCommand}" />

Если IsEnabled == false, кнопка будет отключена.

Пример двухстороннего взаимодействия с Entry

&lt;Entry Text="{Binding UserInput}" /&gt;
<Button Text="Обработать"
Command="{Binding ProcessInputCommand}"
CommandParameter="{Binding Text, Source={x:Reference MyEntry}}" />

Асинхронные команды

Для асинхронных действий в командах рекомендуется использовать обертки или сторонние реализации AsyncCommand.

Простой способ:

public ICommand LoadDataCommand { get; }
public MainViewModel()
{
LoadDataCommand = new Command(async () => await LoadDataAsync());
}
private async Task LoadDataAsync()
{
// Асинхронная логика
}

Или использовать стороннюю реализацию AsyncCommand (например, из MvvmHelpers):

public IAsyncCommand LoadCommand { get; }
LoadCommand = new AsyncCommand(LoadDataAsync);

Примеры использования в разных элементах UI

MenuItem:

<MenuItem Text="Удалить"
Command="{Binding DeleteCommand}"
CommandParameter="{Binding .}" />

ToolbarItem:

<ToolbarItem Text="Добавить"
Command="{Binding AddItemCommand}" />

SwipeView:

&lt;SwipeView&gt;
&lt;SwipeView.RightItems&gt;
&lt;SwipeItems&gt;
<SwipeItem Text="Удалить"
Command="{Binding DeleteCommand}"
CommandParameter="{Binding .}" />
&lt;/SwipeItems&gt;
&lt;/SwipeView.RightItems&gt;
&lt;/SwipeView&gt;

Преимущества использования Command

Особенность Описание
Чистота архитектуры UI не знает о логике, логика не знает о UI
--- ---
Тестируемость Методы, связанные с UI, можно тестировать отдельно
--- ---
Повышенная читаемость Всё взаимодействие сосредоточено в ViewModel
--- ---
Поддержка параметров Команды могут обрабатывать контекстные параметры
--- ---
Условное выполнение Через CanExecute()
--- ---

Расширенные сценарии

Command внутри коллекции (например, в ListView)

Когда используется ListView, каждая строка может иметь свою ViewModel или элемент, и к нему можно привязать команду:

&lt;ListView ItemsSource="{Binding Items}"&gt;
&lt;ListView.ItemTemplate&gt;
&lt;DataTemplate&gt;
&lt;ViewCell&gt;
&lt;StackLayout Orientation="Horizontal"&gt;
&lt;Label Text="{Binding Name}" /&gt;
<Button Text="Удалить"
Command="{Binding BindingContext.DeleteItemCommand, Source={x:Reference Page}}"
CommandParameter="{Binding .}" />
&lt;/StackLayout&gt;
&lt;/ViewCell&gt;
&lt;/DataTemplate&gt;
&lt;/ListView.ItemTemplate&gt;
&lt;/ListView&gt;

Использование RelayCommand или DelegateCommand

Можно создать свою реализацию ICommand, если нужен более гибкий контроль. Пример реализации:

public class RelayCommand : ICommand
{
private readonly Action&lt;object&gt; \_execute;
private readonly Func&lt;object, bool&gt; \_canExecute;
public RelayCommand(Action&lt;object&gt; execute, Func&lt;object, bool&gt; canExecute = null)
{
\_execute = execute;
\_canExecute = canExecute;
}
public bool CanExecute(object parameter) => \_canExecute?.Invoke(parameter) ?? true;
public void Execute(object parameter) => \_execute(parameter);
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() =>
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

Советы по работе с командами

  • Не используйте обработчики событий в code-behind, если используете MVVM.

  • Вызывайте ChangeCanExecute() при изменении условий выполнения команды.

  • Используйте команды даже для диалогов и навигации, чтобы логика оставалась в ViewModel.

  • При использовании асинхронных операций избегайте async void — это усложняет отладку и может вызвать ошибки.

Команды в Xamarin.Forms позволяют элегантно реализовывать взаимодействие пользователя с интерфейсом, сохраняя принципы чистой архитектуры и MVVM. Они обеспечивают надежное и масштабируемое связывание событий UI с логикой представления и позволяют грамотно управлять доступностью действий и передавать параметры, необходимые для их выполнения.