Содержание
В предыдущей части мы познакомились с шаблоном 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
и пустой список проектов:Как только мы нажмем кнопку «Добавить» выполнится метод
Execute
нашей команды и мы увидим:Можно добавить и другие проекты в список, заполняя
Entry
:
Итого
Для того, чтобы обрабатывать какие-либо действия в пользовательском интерфейсе, в MVVM используется механизм команд. Команда — это специальный объект, реализующий интерфейс ICommand
и выполняющий какую-либо функцию в приложении. Для использования команд в приложении, элементы управления привязываются к командам, используя свойство Command
.