Одни модели представления могут использовать другие. Разбиение функционала на несколько моделей представления позволяет более четко очертить задачу для определенной 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 соответственно, тем самым сохраняя отредактированные данные.
Хотя приложение работает, но класс 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>
И результат приложения будет тем же, что и ранее, но код теперь будет чуть более структурированным.