MVVM в .NET MAUI. Команды (ICommand)

В предыдущей части мы познакомились с шаблоном MVVM и даже написали небольшое приложение, использующее все три компонента — View, ViewModel и Model. В этом приложении мы продемонстрировали лишь часть работы — передачу данных от модели к представлению. Однако на ViewModel возлагаются также и задачи по обработке событий, сообщающих об изменении модели и передаче событий об изменении данных в пользовательский интерфейс. Для решения этих задач в .NET MAUI используются команды.

Команды в .NET MAUI

Команда в .NET MAUI — это специальный объект, связанный (обычно) с какой-либо функцией приложения и реализующий интерфейс ICommand, к которому привязывается пользовательский интерфейс.

Интерфейс System.Windows.Input.ICommand выглядит следующим образом:

public interface ICommand
{
    event EventHandler? CanExecuteChanged;
    bool CanExecute(object? parameter);
    void Execute(object? parameter);
}
  • Событие CanExecuteChanged генерируется каждый раз, когда происходят какие-либо изменения, который могут повлиять на выполнение команды.
  • Метод CanExecute() возвращает true, если команда может быть выполнена.
  • Метод Execute() выполняет команду.

Для реализации системы команд в приложении мы можем либо создать свой класс, наследующий ICommand, либо воспользоваться классами Command или Command<T> для создания объектов своих команд.

Пример использования команд в приложении .NET MAUI

Добавление команды в модель представления

Чтобы продемонстрировать работу команд в .NET MAUI воспользуемся примером из предыдущей части. На данный момент у нас определена модель представления следующего вида:

public class ProjectViewModel : INotifyPropertyChanged
{
    private Project project = new() 
    {
        Id = 1,
        Name = "MVVM в .NET MAUI",
        Description = "Изучаем применение шаблона MVVM в .NET MAUI",
        Author = "Vlad (csharp.webdelphi.ru)",
    };


    public string Name
    {
        get => project.Name;
        set
        {
            if (project.Name != value)
            {
                project.Name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Id
    {
        get => project.Id;
        set
        {
            if (project.Id != value)
            {
                project.Id = value;
                OnPropertyChanged();
            }
        }
    }
    public string Description
    {
        get => project.Description;
        set
        {
            if (project.Description != value)
            {
                project.Description = value;
                OnPropertyChanged();
            }
        }
    }
    public string Author
    {
        get => project.Author;
        set
        {
            if (project.Author != value)
            {
                project.Author = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Доработаем этот класс, добавив в него команду:

public class ProjectViewModel : INotifyPropertyChanged
{
    private Project project = new() 
    {
        Id = 1,
        Name = "MVVM в .NET MAUI",
        Description = "Изучаем применение шаблона MVVM в .NET MAUI",
        Author = "Vlad (csharp.webdelphi.ru)",
    };

    public ObservableCollection<Project> Projects { get; set; } = [];

    public ICommand AddCommand { get; set; }

    public ProjectViewModel() 
    {
        AddCommand = new Command(() => 
        {
            Project project = new()
            {
                Id = Projects.Count + 1,
                Name = this.Name,
                Description = this.Description,
                Author = this.Author
            };

            Projects.Add(project);
            Id = -1;
            Name = string.Empty;
            Description = string.Empty;
            Author = string.Empty;
        });
    }

    public string Name
    {
        get => project.Name;
        set
        {
            if (project.Name != value)
            {
                project.Name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Id
    {
        get => project.Id;
        set
        {
            if (project.Id != value)
            {
                project.Id = value;
                OnPropertyChanged();
            }
        }
    }
    public string Description
    {
        get => project.Description;
        set
        {
            if (project.Description != value)
            {
                project.Description = value;
                OnPropertyChanged();
            }
        }
    }
    public string Author
    {
        get => project.Author;
        set
        {
            if (project.Author != value)
            {
                project.Author = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

Посмотрим на изменения. Во-первых теперь класс ProjectViewModel хранит в своих свойствах не только информацию по проекту, но и список этих проектов:

public ObservableCollection<Project> Projects { get; set; } = [];

при этом, чтобы мы могли получать сообщения об изменении этой коллекции, свойство Projects определено именно как ObservableCollection<Project>(информацию об этом классе см. здесь), а не обычный List<T>.

Во-вторых, мы определили новое свойство типа ICommand:

public ICommand AddCommand { get; set; }

а сам объект команды мы создаем непосредственно в конструкторе класса:

public ProjectViewModel() 
{
    AddCommand = new Command(() => 
    {
        Project project = new()
        {
            Id = Projects.Count + 1,
            Name = this.Name,
            Description = this.Description,
            Author = this.Author
        };

        Projects.Add(project);
        Id = -1;
        Name = string.Empty;
        Description = string.Empty;
        Author = string.Empty;
    });
}

Здесь мы воспользовались одно из версий конструктора класса Command. Наша команда создает новый объект типа Project, используя свойства класса ProjectViewModel, затем добавляет полученный объект в коллекцию Projects и сбрасывает свойства класса до значений по умолчанию. Теперь воспользуемся этой командой в нашем приложении.

Использование команды в XAML

Чтобы использовать команду в приложении .NET MAUI мы должны использовать привязку. На данный момент, наша модель представления зарегистрирована в приложении как сервис и передается в представление через конструктор MainPage и нам остается только немного доработать код XAML страницы. Изменим код MainPage.xaml следующим образом:

<?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="MvvmExample.MainPage"
             xmlns:models ="clr-namespace:MvvmExample.Models"
             xmlns:local ="clr-namespace:MvvmExample.ViewModels"
             x:DataType="local:ProjectViewModel">

    <ScrollView>
        <VerticalStackLayout>
            <Label Text="Название проекта" />
            <Entry Text="{Binding Name}" FontAttributes="Bold" />
            <Label Text="Описание" />
            <Entry Text="{Binding Description}" FontAttributes="Bold" />
            <Label Text="Автор" />
            <Entry Text="{Binding Author}" FontAttributes="Bold" />
            <Button Text="Добавить" Command="{Binding AddCommand}"/>

            <CollectionView ItemsSource="{Binding Projects}">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="{x:Type models:Project}">
                        <TableView>
                            <TableRoot>
                                <TableSection Title="{Binding Name}">
                                    <TextCell Text="{Binding Id}"/>
                                    <TextCell Text="{Binding Description}"/>
                                    <TextCell Text="{Binding Author}"/>
                                </TableSection>
                            </TableRoot>
                        </TableView>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Первое, что мы сделали — это добавили пространство имен

xmlns:models ="clr-namespace:MvvmExample.Models"

чтобы использовать тип Project при определении шаблона данных. Далее, мы привязали нашу команду AddCommand к свойству Command кнопки:

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

И в заключении мы определили элемент CollectionView для вывода всего списка проектов:

<CollectionView ItemsSource="{Binding Projects}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="{x:Type models:Project}">
            <TableView>
                <TableRoot>
                    <TableSection Title="{Binding Name}">
                        <TextCell Text="{Binding Id}"/>
                        <TextCell Text="{Binding Description}"/>
                        <TextCell Text="{Binding Author}"/>
                    </TableSection>
                </TableRoot>
            </TableView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Обратите внимание, что использование механизма команд позволяет нам не использовать события той же кнопки Button, связывая пользовательский интерфейс с моделью данных через нашу модель представления ProjectViewModel. Запустим приложение и проверим его работу.

Сразу после запуска приложения мы увидим заполненные элементы Entry и пустой список проектов:Команды в .NET MAUIКак только мы нажмем кнопку «Добавить» выполнится метод Execute нашей команды и мы увидим:Команды в .NET MAUIМожно добавить и другие проекты в список, заполняя Entry:Команды в .NET MAUI

Итого

Для того, чтобы обрабатывать какие-либо действия в пользовательском интерфейсе, в MVVM используется механизм команд. Команда — это специальный объект, реализующий интерфейс ICommand и выполняющий какую-либо функцию в приложении. Для использования команд в приложении, элементы управления привязываются к командам, используя свойство Command.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии