Сводная информация о Главе 18. MVVM

Примечание.

Эта книга была опубликована весной 2016 года и с тех пор не обновлялась. Многое в этой книге остается ценным, но некоторые материалы устарели, а некоторые разделы перестали быть полностью верными или полными.

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

Взаимосвязи в MVVM

Приложение MVVM имеет три уровня.

  • Модель предоставляет базовые данные, иногда через файлы или веб-доступ.
  • Представление выполняет роль пользовательского интерфейса или уровня представления, и обычно реализуется в XAML.
  • Модель представления (ViewModel) соединяет Модель с Представлением.

Модель ничего не знает о ViewModel, а ViewModel — о Представлении. Эти три уровня обычно взаимодействуют друг с другом по следующим механизмам?

Представление, ViewModel, Представление

Во многих небольших программах (а иногда даже в больших) Модель может отсутствовать или быть полностью встроенной во ViewModel.

ViewModel и привязка данных

Чтобы участвовать в привязке данных, ViewModel должна уметь информировать Представление об изменении свойств ViewModel. Для этого ViewModel реализует интерфейс INotifyPropertyChanged в пространстве имен System.ComponentModel. Это относится скорее к .NET, чем к Xamarin.Forms. (Обычно ViewModel пытается остаться независимой от платформы.)

Интерфейс INotifyPropertyChanged объявляет одно событие с именем PropertyChanged, которое сигнализирует об изменении свойства.

Часы ViewModel

DateTimeViewModel из библиотеки Xamarin.FormsBook.Toolkit определяет свойство типа DateTime, значение которого изменятся по таймеру. Этот класс реализует INotifyPropertyChanged и активирует событие PropertyChanged при каждом изменении свойства DateTime.

Пример MvvmClock создает экземпляр этой ViewModel и использует привязку данных к ViewModel для отображения сведений о текущих дате и времени.

Интерактивные свойства во ViewModel

Свойства ViewModel могут быть более интерактивными, как показано в классе SimpleMultiplierViewModel, который входит в пример SimpleMultiplier. Привязки данных предоставляют значения множителей из двух элементов Slider и отображают произведение с помощью Label. При этом вы можете вносить достаточно существенные изменения в этот интерфейс через XAML, не вызывая никаких изменений во ViewModel или файле кода программной части.

ViewModel для цвета

ColorViewModel из библиотеки Xamarin.FormsBook.Toolkit включает в себя цветовые модели RGB и HSL. Его использование представлено в примере HslSliders:

Три снимка экрана с набором инструментов

Упрощение ViewModel

Вы можете упростить код ViewModel, определив метод OnPropertyChanged с атрибутом CallerMemberName, который автоматически получает имя вызывающего свойства. Класс ViewModelBase из библиотеки Xamarin.FormsBook.Toolkit выполняет именно эту функцию и служит базовым классом для ViewModel.

Командный интерфейс

MVVM работает с привязками данных, а они в свою очередь — со свойствами, поэтому возможности MVVM довольно ограничены при обработке события Clicked из Button или события Tapped из TapGestureRecognizer. Чтобы ViewModel могла обрабатывать такие события, Xamarin.Forms поддерживает командный интерфейс.

Командный интерфейс объявляет себя в Button с двумя открытыми свойствами:

Чтобы поддерживать командный интерфейс, во ViewModel нужно определить свойство с типом ICommand и выполнить для него привязку данных к свойству Command из Button. Интерфейс ICommand объявляет два метода и одно событие:

  • метод Execute с аргументом типа object;
  • метод CanExecute с аргументом типа object, который возвращает bool;
  • событие CanExecuteChanged.

Внутри ViewModel присваивает каждому свойству с типом ICommand значение экземпляра класса, который реализует интерфейс ICommand. Через привязку данных Button изначально вызывает метод CanExecute и отключается, если этот метод возвращает false. Также она назначает обработчик для события CanExecuteChanged и вызывает CanExecute при активации этого события. Если включен Button, он вызывает метод Execute при каждом нажатии Button.

Возможно, у вас уже есть существующие ViewModels, созданные раньше Xamarin.Forms, и они даже могут поддерживать командный интерфейс. Для новых ViewModels, которые будут использоваться только с Xamarin.Forms, Xamarin.Forms предоставляет класс Command и класс Command<T>, который реализует интерфейс ICommand. Для аргументов методов Execute и CanExecute используется универсальный тип.

Простое выполнение метода

Пример PowersOfThree демонстрирует применение командного интерфейса из ViewModel. Класс PowersViewModel определяет два свойства с типом ICommand, а также два частных свойства, которые он передает в простейший конструктор Command. Эта программа содержит привязки данных из ViewModel к свойствам Command двух элементов Button.

Элементы Button можно свободно заменить в XAML объектами TapGestureRecognizer, не меняя код.

Калькулятор, ну почти

Пример AddingMachine использует оба метода класса ICommand: Execute и CanExecute. Он использует также класс AdderViewModel из библиотеки Xamarin.FormsBook.Toolkit. ViewModel содержит шесть свойств с типом ICommand. Они инициализируются из конструктора Command и конструктора Command класса Command, а также конструктора Command<T> класса Command<T>. Все числовые кнопки на сумматоре привязаны к свойству, которое инициализируется значением Command<T>, а аргумент string — к Execute, где CanExecute обозначает конкретную кнопку.

ViewModel и жизненный цикл приложения

AdderViewModel из примера AddingMachine определяет также два метода с именами SaveState и RestoreState. Эти методы вызываются из приложения, когда оно переходит в спящий режим и пробуждается из спящего режима.