MVVM в .NET MAUI. Введение

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

Общие сведения о шаблоне MVVM

Шаблон MVVM можно представить в виде следующей схемы:

MVVM
MVVM

Представления (View)

Представление отвечает за определение структуры, макета и внешнего вида того, что пользователь видит на экране. В идеальном случае, каждое представление определяется в XAML с ограниченным кодом, который не содержит бизнес-логику. Однако в некоторых случаях код программной части может содержать логику пользовательского интерфейса, которая реализует визуальное поведение, которое трудно выразить в XAML, например анимации.

Модель представления (ViewModel)

Модель представления реализует свойства и команды, к которым представление может привязать данные, и уведомляет представление о любых изменениях состояния с помощью событий уведомлений об изменении. Свойства и команды, предоставляемые моделью представления, определяют функциональные возможности, предоставляемые пользовательским интерфейсом (логику), а представление определяет способ отображения этой функции.

Модель представления также отвечает за координацию взаимодействия представления с любыми необходимыми классами моделей.

Каждая модель представления предоставляет данные из модели в форме, которую представление может легко использовать. Для этого модель представления иногда выполняет преобразование данных. Размещение этого преобразования данных в модели представления является хорошей идеей, так как она предоставляет свойства, к которым может привязаться представление. Например, модель представления может объединить значения двух свойств, чтобы упростить отображение в представлении.

Модель

Классы модели — это не визуальные классы, которые содержат какие-либо данные приложения. Обычно, модель рассматривается как модель домена приложения, которая включает модель данных вместе с бизнес-логикой и логикой проверки.

Реализация MVVM в .NET MAUI

Простота и легкость реализации шаблона MVVM в приложениях .NET MAUI стала возможной, благодаря встроенному механизму привязки. Как правило, свойству BindingContext визуального элемента (View) присваивается объект, реализующий в приложении модель представления (ViewModel), которая, в свою очередь, взаимодействует с различными моделями приложения.

Пример проекта с MVVM

Чтобы продемонстрировать применение этого архитектурного шаблона в приложении .NET MAUI создадим новый проект, добавим в него папку Models и разместим в этой папке класс:

public class Project
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string Author { get; set; }
}

Этот класс будет выступать в качестве модели. Теперь создадим модель представления. Для этого добавим в проект папку ViewModels и разместим в нем следующий класс:

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));
    }
}

Этот класс и выступает в качестве ViewModel — он получает данные из модели и предоставляет их в представление в удобном для использования виде — настраивает уведомления об изменении свойств и, при необходимости, модифицирует данные модели.

Третий компонент — представление (View). В качестве представления у нас будет выступать страница MainPage. Теперь нам осталось только связать все три компонента воедино. Для начала, изменим C#-код страницы следующим образом:

using MvvmExample.ViewModels;
namespace MvvmExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            BindingContext = new ProjectViewModel();
        }
    }
}

здесь мы присваиваем в конструкторе свойству BindingContext объект ProjectViewModel. Теперь в 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">

    <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" />
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Теперь все три части связаны и можно проверить результат. Запустите приложение и убедитесь, что в элементах Entry отображаются данные по проекту:

При написании кода XAML вы могли заметить, что редактор XAML «ругается» на то, что не может определить свойство привязки:

хотя приложение прекрасно работает. Чтобы избавиться от таких сообщений, достаточно добавить для страницы атрибут x:DataType следующим образом:

<?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: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" />
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Теперь никаких предупреждений не будет.

Передача значения BindingContext через конструктор

Мы уже знаем, что из себя представляет внедрение зависимостей и то как регистрировать сервисы и запрашивать их. Поэтому для нас не составит труда предоставить нашу модель представления в качестве сервиса. Добавим в MauiProgram.cs следующую строку:

using Microsoft.Extensions.Logging;

using MvvmExample.ViewModels;

namespace MvvmExample;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        builder.Services.AddSingleton<ProjectViewModel>();//регистрируем сервис

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

Теперь модель представления выступает в качестве сервиса и так как этот сервис необходим для работы представления, то мы можем запросить его в конструкторе класса, например, так:

public partial class MainPage : ContentPage
{
    public MainPage(ProjectViewModel model)
    {
        InitializeComponent();
        BindingContext = model;
    }
}

В этом случае, .NET MAUI автоматически запросит необходимый сервис из контейнера DI и вернет его в параметре model конструктора. Такой подход связывания View и ViewModel является достаточно распространенным и мы его, в дальнейшем, будем использовать.

Итого

Архитектурный шаблон MVVM позволяет отделить бизнес-логику приложения от интерфейса. В .NET MAUI шаблон достаточно легко реализуется, благодаря свойству BindingContext визуальных компонентов. Одним из способов передачи значения в BindingContext является использование ViewModel в качестве сервисов.

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