Организация моделей представления

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

Одни модели представления могут использовать другие. Разбиение функционала на несколько моделей представления позволяет более четко очертить задачу для определенной ViewModel, сделать логику приложения более гибкой в плане поддержки и дальнейшего развития.

Например, нам надо совместить на странице отображение и редактирование объектов следующего класса Person:

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

Для этого определим в проекте следующий класс MainViewModel::

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

namespace HelloApp;

public class MainViewModel : INotifyPropertyChanged
{
    Person person = new Person("Tom", 38);
    string name = "";
    int age;
    public event PropertyChangedEventHandler? PropertyChanged;
    public ICommand SaveCommand { get; set; }
    public ICommand EditCommand { get; set; }

    public MainViewModel()
    {
        SaveCommand = new Command (() =>
        {
            PersonName = Name;
            PersonAge= Age;
            Name = "";
            Age = 0;
        });
        EditCommand = new Command(() =>
        {
            Name = PersonName;
            Age = PersonAge;
        });
    }
    public string PersonName
    {
        get => person.Name;
        set
        {
            if (person.Name != value)
            {
                person.Name = value;
                OnPropertyChanged();
            }
        }
    }
    public int PersonAge
    {
        get => person.Age;
        set
        {
            if (person.Age != value)
            {
                person.Age = value;
                OnPropertyChanged();
            }
        }
    }
    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));
    }
}

Здесь модель представления хранит сами данные в виде объекта Person, а также переменные name и age, которые будут хранить редактируемые данные.

Команда EditCommand присваивает значения свойств объекта Person свойствам Name и Age. Таким образом, данные будут передаваться на редактирование.

Команда SaveCommand, наоборот, присваивает значения свойств Name и Age свойствам объекта Person. То есть таким образом, сохраняем сделанные изменения.

В коде MainPage.xaml.cs определяем объект MainViewModel в качестве контекста привязки:

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">
        
        <Label Text="Name" />
        <Label Text="{Binding PersonName}" FontAttributes="Bold" />
        <Label Text="Age" />
        <Label Text="{Binding PersonAge}" FontAttributes="Bold" />
        <Button Text="Edit" WidthRequest="100" HorizontalOptions="Start"
                        Command="{Binding EditCommand}"  />
        <VerticalStackLayout>
            <Entry Placeholder="Enter name" Text="{Binding Name}" />
            <Entry Placeholder="Enter age" Text="{Binding Age}"/>
            <Button Text="Save" WidthRequest="100" HorizontalOptions="Start"
                        Command="{Binding SaveCommand}"  />
        </VerticalStackLayout>
    </VerticalStackLayout>
</ContentPage>

Здксь вначале расположены метки, которые выводят значения свойств объекта Person. По нажатию на кнорку Edit эти данные передаются в текстовые поля ниже (то есть значения свойств PersonName и PersonAge копируются в свойства Name и Age).

По нажатию на кнопку Save срабатывает команда SaveCommand, которая, наборот, копирует значения Name и Age в PersonName и PersonAge соответственно, тем самым сохраняя отредактированные данные.

Организация ViewModel в проекте .NET MAUI и C#, который применяет паттерн MVVM

Хотя приложение работает, но класс MainViewModel фактически представляет две функции - отображение и редактирование данных. Гораздо проще этими данными управлять по отдельности. Так, изменим класс MainViewModel следующим образом:

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

namespace HelloApp;

public class DisplayViewModel : INotifyPropertyChanged
{
    Person person;
    public event PropertyChangedEventHandler? PropertyChanged;
    public DisplayViewModel(Person person) => this.person = person;
    public string Name
    {
        get => person.Name;
        set
        {
            if (person.Name != value)
            {
                person.Name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Age
    {
        get => person.Age;
        set
        {
            if (person.Age != value)
            {
                person.Age = value;
                OnPropertyChanged();
            }
        }
    }
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}
public class EditViewModel : INotifyPropertyChanged
{
    string name = "";
    int age;
    public event PropertyChangedEventHandler? PropertyChanged;
    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));
    }
}
public class MainViewModel
{
    public ICommand SaveCommand { get; set; }
    public ICommand EditCommand { get; set; }
    public DisplayViewModel Person { get; set; }
    public EditViewModel EditData { get; set; }

    public MainViewModel()
    {
        Person = new DisplayViewModel(new Person("Tom",38));
        EditData = new EditViewModel();
        SaveCommand = new Command (() =>
        {
            Person.Name = EditData.Name;
            Person.Age= EditData.Age;
            EditData.Name = "";
            EditData.Age = 0;
        });
        EditCommand = new Command(() =>
        {
            EditData.Name = Person.Name;
            EditData.Age = Person.Age;
        });
    }
}

Теперь для отображения данных применяется класс DisplayViewModel, который хранит отображаемые данные объекта Person. А данные для редактирования перемещены в класс EditViewModel. Основная же модель представления - MainViewModel по прежнему определяет команды и выстпает посредником между DisplayViewModel и EditViewModel. Таким кодом легче управлять и обновлять.

Для применения изменений обновим код страницы 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="HelloApp.MainPage">
    <VerticalStackLayout Padding="5">
        
        <Label Text="Name" />
        <Label Text="{Binding Person.Name}" FontAttributes="Bold" />
        <Label Text="Age" />
        <Label Text="{Binding Person.Age}" FontAttributes="Bold" />
        <Button Text="Edit" WidthRequest="100" HorizontalOptions="Start"
                        Command="{Binding EditCommand}"  />
        <VerticalStackLayout>
            <Entry Placeholder="Enter name" Text="{Binding EditData.Name}" />
            <Entry Placeholder="Enter age" Text="{Binding EditData.Age}"/>
            <Button Text="Save" WidthRequest="100" HorizontalOptions="Start"
                        Command="{Binding SaveCommand}"  />
        </VerticalStackLayout>
    </VerticalStackLayout>
</ContentPage>

И результат приложения будет тем же, что и ранее, но код теперь будет чуть более структурированным.

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