Команды и взаимодействие с пользователем в MVVM

Последнее обновление: 24.01.2023

В прошлой теме описывался паттерн MVVM, позволяющий отображать связанные данные. Однако, как правило, необходимо не только отображать данные, но и использовать какую-то логику взаимодействия с пользователем, обрабатывать пользовательский ввод. Рассмотрим, как это сделать в рамках паттерна MVVM.

Ключевой идеей паттерна является взаимодействие с моделью через ViewModel, то есть в данном случае использование событий визуальных компонентов и их обработчиков нежелательно. Чтобы решить эту задачу, в платформе .NET MAUI имеется механизм команд, которые представляют реализацию интерфейса System.Windows.Input.ICommand:

public interface ICommand 
{ 
	void Execute(object? arg); 
	bool CanExecute(object? arg); 
	event EventHandler? CanExecuteChanged; 
}

Метод Execute() выполняет команду.

Метод CanExecute возвращает true, если команда может быть выполнена.

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

Для создания команды можно использовать, как минимум, два способа

  • Определить класс, который реализует интерфейс ICommand, и затем создать конкретные команды - объекты этого класса.

  • Использовать класс Command или его обобщенную версию Command<T>, которые включенны в .NET MAUI и которые реализуют интерфейс ICommand

ViewModel может определять свойства типа ICommand. Затем подобные свойства можно привяать к свойству Command кнопки или другого визуального компонента, который поддерживает комманды. Например, когда пользователь нажимает кнопку, внутри Button вызывается метод Execute соответствующей команды. И мы можем либо использовать обработку события нажатия кнопки, либо привязать команду и также выполнять некоторые действия.

Реализация ICommand

Рассмотрим простейшее применение команд. Допустим, данные у нас представлены классом Person:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name; Age = age;
    }
}

В качестве ViewModel определим класс MainViewModel:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace HelloApp;

public class MainViewModel : INotifyPropertyChanged
{
    string name = "";
    int age;
    public event PropertyChangedEventHandler? PropertyChanged;
    public ICommand AddCommand { get; set; }
    public ObservableCollection<Person> People { get; } = new();

    public MainViewModel()
    {
        // устанавливаем команду добавления
        AddCommand = new Command(() =>
        {
            People.Add(new Person(Name, Age));
            Name = "";
            Age = 0;
        });
    }
    public string Name
    {
        get => name;
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Age
    {
        get => age;
        set
        {
            if (age != value)
            {
                age = value;
                OnPropertyChanged();
            }
        }
    }
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Класс MainViewModel хранит поля name и age и надстройки над ними - свойства Name и Age, через которые пользователь будет ввводить новые данные. Также класс определяет свойство People - коллекцию ObservableCollection, в которую будут добавляться данные.

Для добавления данных определено свойство-команда AddCommand, которая устанавливается в конструкторе

AddCommand = new Command(() =>
{
    People.Add(new Person(Name, Age));
    Name = "";
    Age = 0;
});

Для установки команды в конструктор класса Command передается делегат Action, который представляет выполняемое командой действие. В данном случае берем значения свойств Name и Age (которые представляют введенные пользователем данные), создаем по ним объект Person и добавляем его в коллекцию People. Таким образом, будет выполняться добавление данных. После добавления сбрасываем значения свойств Name и Age.

Стоит отметить, что конструктор Command может имеет несколько версий с разным набором параметров.

В классе странице MainPage выполним привязку к этой модели представления:

namespace HelloApp;

public partial class MainPage : ContentPage
{
    public MainPage()
	{
		InitializeComponent();
        BindingContext = new MainViewModel();
    }
}

А в коде MainPage.xaml установим привязку отдельных элементов графического инстерфейса к свойствам MainViewModel:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HelloApp.MainPage">
    <VerticalStackLayout Padding="5">
        <VerticalStackLayout>
            <Entry Placeholder="Enter name" Text="{Binding Name}" />
            <Entry Placeholder="Enter age" Text="{Binding Age}"/>
            <Button Text="Save" WidthRequest="100" HorizontalOptions="Start"
                        Command="{Binding AddCommand}"  />
        </VerticalStackLayout>
        <ListView ItemsSource="{Binding People}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <VerticalStackLayout>
                            <Label Text="{Binding Name}" />
                            <Label Text="{Binding Age}" />
                        </VerticalStackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </VerticalStackLayout>
</ContentPage>

Вначале страницы определено два текстовых поля, которые привязаны к свойствам Name и Age. А по нажатию кнопки Add срабатывает команда AddCommand. Для привязки команды у кнопки, как и у ряда других элементов есть свойство Command:

<Button Text="Save" WidthRequest="100" HorizontalOptions="Start"
    Command="{Binding AddCommand}"  />

Таким образом, по нажатию на кнопку введенные в текстовые поля данные будут добавлены в список People в MainViewModel.

Чуть ниже определен элемент ListView, который выводит список People.

Результат работы приложения:

Команды и ICommand в реализации паттерна MVVM в приложении на .NET MAUI и C#

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850