Использование команд в модели представления
- 5 мин
Вы узнали, как получить данные из модели представления в пользовательский интерфейс и как можно использовать двусторонняя привязка для возврата данных в модели представления.
Использование двухсторонней привязки, как это — предпочтительный способ реагировать на изменения пользовательского интерфейса в случае изменения данных. Очень многие вещи, которые планировалось бы обрабатывать как события, могут быть обработаны с помощью двусторонних привязок данных и шаблона моделиView-ViewModel (MVVM). Другие примеры — это такие вещи Switch.IsToggled
, как и Slider.Value
, которые могут быть отражены в нашем представлении в виде логического или целочисленного значения, без необходимости использовать события.
Однако некоторые действия, например активация Button
или MenuItem
, не имеют прямой связи с изменением данных. Для них по-прежнему требуется событийная обработка. Так как эти компоненты пользовательского интерфейса обычно вызывают некоторую логику с данными, мы хотим, чтобы эта логика в модели представления. Но мы не хотим обрабатывать их как Clicked
и Selected
события в коде, если это возможно. Мы хотим как можно больше быть в режиме просмотра, таким образом, это можно проверить.
Использование шаблона команды
Многие элементы управления .NET MAUI, имеющие эту привязку взаимодействия к свойству ICommand
, предоставляющего интерфейс. Это свойство, скорее всего, называется Command
. Элемент управления является одним из примеров Button
:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />
Элемент управления знает, когда вызывать команду. Например, кнопка вызывает команду при нажатии. Команда в этом примере привязана к GiveBonus
свойству viewmodel. Тип свойства должен реализовать ICommand
интерфейс. Код будет выглядеть примерно так:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
...
}
Интерфейс ICommand
имеет Execute
метод, который вызывается при нажатии кнопки. Таким образом, ICommand.Execute
напрямую заменяет код обработки события Button.Click
.
Полный ICommand
интерфейс имеет два других метода: CanExecute
и CanExecuteChanged
они используются для определения того, должен ли элемент управления отображаться включено или отключено.
Например, если CanExecute
возвращает значение false, кнопка отображается как неактивная (затененная).
Вот как ICommand
выглядит интерфейс в C#:
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Использование класса Command
Этот шаблон команды позволяет четко отделять работу пользовательского интерфейса от его реализации. Но он может усложнить код, если требуется создать отдельный класс для реализации каждого обработчика событий.
Вместо создания нескольких пользовательских классов, реализующих интерфейс, обычно используется Command
или Command<T>
. Эти классы реализуют ICommand
, но предоставляют его поведение в виде свойств в представлении, которые можно задать. Таким образом, мы можем реализовать GiveBonus
свойство, описанное ранее, полностью в нашем классе viewmodel:
public class EmployeeViewModel : INotifyPropertyChanged
{
public ICommand GiveBonusCommand {get; private set;}
public EmployeeViewModel(Employee model)
{
GiveBonusCommand = new Command(GiveBonusExecute, GiveBonusCanExecute)
}
void GiveBonusExecute()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
}
}
В этом коде поведение Execute
предоставляется методом GiveBonusExecute
. А CanExecute
— методом GiveBonusCanExecute
. Делегаты этих методов передаются конструктору Command
. В этом примере отсутствует реализация для CanExecuteChanged
.
Упрощение с помощью набора средств MVVM
Библиотека средств MVVM содержит реализации известных ICommand
как RelayCommand
и AsyncRelayCommand
. Он также предоставляет генераторы источников для упрощения этого кода. В следующем примере будет создан параметр метода для выполнения и вызова, чтобы узнать, GiveBonusCommand
может ли он выполняться. Атрибут [RelayCommand]
используется в методе GiveBonus
, и он создаст GiveBonusCommand
этот атрибут. Кроме того, присвоив CanExecute
свойству атрибут имя метода, который мы хотим подключить к CanExecute
методу ICommand
, он создаст код для настройки для нас.
public partial class EmployeeViewModel : ObservableObject
{
public EmployeeViewModel(Employee model)
{
}
[RelayCommand(CanExecute = nameof(GiveBonusCanExecute))]
void GiveBonus()
{
//logic for giving bonus
}
bool GiveBonusCanExecute()
{
//logic for deciding if "give bonus" button should be enabled.
return true;
}
}
Набор средств MVVM также обрабатывает async
методы, распространенные в программировании .NET.
Команды с параметрами
Интерфейс ICommand
принимает object
параметр для CanExecute
методов и Execute
методов. .NET MAUI реализует этот интерфейс без проверки типа через Command
класс. Делегаты, присоединенные к команде, должны выполнять собственную проверку типа, чтобы убедиться, что передан правильный параметр. .NET MAUI также предоставляет реализацию Command<T>
, в которой задан тип ожидаемого параметра. При создании команды, которая принимает один тип параметра, используйте Command<T>
.
Элементы управления .NET MAUI, реализующие шаблон команды, предоставляют CommandParameter
это свойство. Задав это свойство, можно передать параметр команде при вызове или Execute
при проверке CanExecute
метода для состояния.
В этом примере строковое значение "25" отправляется в команду:
<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" CommandParameter="25" />
Команда должна интерпретировать и преобразовывать этот строковый параметр. Существует множество способов предоставления строго типизированного параметра.
Вместо использования синтаксиса атрибутов для определения
CommandParameter
используйте элементы XAML.<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}"> <Button.CommandParameter> <x:Int32>25</x:Int32> </Button.CommandParameter> </Button>
Привязка
CommandParameter
экземпляра правильного типа.Если значение
CommandParameter
привязано к неправильному типу, примените преобразователь для преобразования значения в правильный тип.