Основы MVVM
Цель работы: освоить основные понятия MVVM
Основы MVVM
Простейшее приложение для Windows Phone, всеми аспектами которого занимается один разработчик, вполне можно написать, не задумываясь о том, чтобы разделять код, имеющий отношение к визуализации данных, к их обработке и получению из некоторых источников. Приложение, в котором большая часть кода, ответственного за всю его функциональность, собрана в программном файле к его начальной странице (особенно, когда эта функциональность требует пары десятков строк кода), будет работать. Но небольшой рост сложности приложения приводит к тому, что таким кодом становится очень неудобно управлять. Его неудобно расширять, тестировать, неудобно привлекать других программистов к работе над приложением. Небольшое изменение в коде может повлечь за собой множество неожиданных, неприятных последствий. Идея разделения кода, который ответственен за работоспособность различных подсистем приложения, это одна из идей, которая легла в основу создания шаблонов проектирования приложений. Один из таких шаблонов, получивший популярность в последние годы, называется MVVM, Model-View-ViewModel, Модель-Представление-Модель представления. Этот шаблон используют на разных платформах, он универсален и позволяет создавать крупные программные проекты, обладающие чёткой и понятной структурой. Для того чтобы приступить к использованию MVVM, нужно понять сущность некоторых основных понятий, которые имеют к отношение к этому шаблону проектирования.
Очень кратко сущность MVVM можно выразить так: "Модель содержит исходные данные, которые готовятся к выводу с помощью модели представления и отображаются пользователю с помощью представления".
Рассмотрим это более подробно.
Представление (View). Это та часть приложения, которая формирует пользовательский интерфейс и отвечает за взаимодействие с пользователем. К представлению относится XAML-разметка страницы, отображаемые на странице элементы управления, анимации, которые применяются к элементам управления и логика, которая имеет отношение к интерфейсу. В идеальном варианте представление – это исключительно XAML-код, который описывает страницу и элементы управления, а в файле программного кода к этой странице содержится лишь конструктор, который вызывается при создании страницы. Для отображения интерфейса, однако, часто требуются какие-либо специфические операции, которые так же реализуют в файлах кода страниц. Представление, конечно, не используется автономно, никому не нужен интерфейс, который не отображает полезных данных и не способен обеспечить взаимодействие пользователя с этими данными. В том, что касается визуализации каких-либо списков данных, а подобные интерфейсы весьма популярны, можно отметить использование шаблонов вывода данных. Шаблон может быть использован множество раз для форматирования списков данных переменной длины, выводимых в интерфейс. Взаимодействие представления с логикой приложения, которая находится на более "глубоких" уровнях MVVM, в частности – с моделью представления, обеспечивают механизмы привязки данных и команд.
Привязка данных. Привязка данных концептуально выглядит следующим образом. Имеется некий элемент управления, например – текстовое поле, в который мы хотим вывести некоторые данные. В XAML-коде, описывающем данное поле мы настраиваем связь этого поля с нужным нам свойством некоего объекта, который содержит данные, которые надо вывести в поле. Текстовое поле называют целевым объектом привязки или приемником, объект, содержащий данные, называют источником данных. Существуют разные типы привязки данных. В частности, так называемая односторонняя (one way) привязка данных приводит к тому, что информация берется из источника, направляется в приемник, при этом, если в приемнике происходит изменение данных, эти изменения не влияют на источник. Но если информация изменилась в источнике данных, изменения отразятся и на приемнике. При двусторонней привязке (two way) изменения, которым данные подвергнуться в приемнике, отражаются и на источнике данных. При этом описание привязок данных осуществляется в XAML-коде страницы, то есть, программный код для реализации этого механизма не нужен. Существуют и другие типы привязки, например, одноразовая (One time) – данные из источника передаются в приемник один раз, например, при смене контекста данных или при загрузке приложения, затем связь между ними не поддерживается. Передачу данных между источником и приемником выполняют механизмы системы привязки данных. К привязке данных относится такое понятие как конвертер данных – программный механизм, которые выполняет преобразование данных.
Привязка команд. Если из пользовательского интерфейса нужно произвести какое-либо управляющее воздействие, эту операцию реализуют с помощью привязки команд. Код команд определен в модели представления, а вызываются они из пользовательского интерфейса. В идеале, как и в случае с привязкой данных, привязка команд осуществляется в XAML-коде.
Модель представления. Модель представления связывает исходные данные, представленные моделью, и их визуальное представление. При этом модель представления не обладает "знаниями" о том, как именно данные используются в представлении – то есть – в интерфейсе приложения. Задача модели представления заключается в подготовке данных к отображению и в предоставлении этих данных представлению, то есть – интерфейсным механизмам – через систему привязки данных. То же самое касается команд. В модели представления может быть определен некий метод, который вызывается каким-либо элементом управления из представления, но этот метод не имеет информации о том, какой именно элемент управления его вызывает. Благодаря подобному разделению представления (интерфейса) и логики представления (модели представления) и достигается гибкость работы над различными частями приложения, созданного по методологии MVVM.
Модель. Модель ответственна за работу с исходными данными. Модель выполняет загрузку данных из внешних источников, их сохранение, при модификации в приложении, она предоставляет эти данные модели представления, которая приводит их к виду, подходящему для передачи представлению – то есть – для отображения в пользовательском интерфейсе.
При проектировании приложений с использованием шаблона MVVM очень важно правильно распределить функциональность приложения между его моделью, представлением и моделью представления.
Если рассматривать реализацию MVVM-приложения с использованием стандартных средств, доступных на платформе Windows Phone, то здесь можно отметить следующие особенности.
- Классы моделей и моделей представления обычно реализуют интерфейс INotifyPropertyChanged (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.componentmodel.inotifypropertychanged%28v=vs.105%29.aspx). Благодаря его использованию обеспечивается работоспособность системы привязки данных. Сущность его использования заключается в том, что его реализация позволяет отправлять уведомления с помощью события PropertyChanged (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.componentmodel.inotifypropertychanged.propertychanged%28v=vs.105%29.aspx), сигнализирующие об изменении данных приложения. Эти изменения, благодаря системе привязки данных, в итоге, "доходят" от модели (то есть – от уровня исходных данных, на которых основано приложение), через модель представления к пользовательскому интерфейсу. Нужно отметить, что некоторые типы, например ObservableCollection<T> (http://msdn.microsoft.com/en-us/library/windowsphone/develop/ms668604%28v=vs.105%29.aspx), представляющий динамическую коллекцию, подходящую для привязки данных, так же предусматривают реализацию INotifyPropertyChanged.
- Классы моделей, если им нужно снабжать другие классы информацией об ошибках в данных, выполнять проверку данных и сообщать о её результатах, реализуют интерфейс INotifyDataErrorInfo (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.componentmodel.inotifydataerrorinfo%28v=vs.105%29.aspx).
- Привязка данных может осуществляться как в XAML-разметке, так и в программном коде. Первый метод предпочтительнее, так как он, в идеале, позволяет создавать пользовательские интерфейсы, всё описание которых сосредоточено в XAML-файлах, файлы кода подобных интерфейсов содержат лишь конструкторы. В привязке данных существует понятие контекст данных (data context) он определяет объект, к которому осуществляется привязка. Свойство DataContext (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.frameworkelement.datacontext%28v=vs.105%29.aspx) класса FrameworkElement (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.frameworkelement%28v=vs.105%29.aspx) позволяет определять контекст данных для элементов интерфейса, в иерархии наследования которых есть FrameworkElement. Именно этот класс ответственен за возможность элементов пользовательского интерфейса участвовать в операциях привязки данных. Когда определен контекст данных, в XAML привязка осуществляется с помощью расширения разметки {Binding ...}. Здесь используется класс Binding (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.data.binding%28v=vs.105%29.aspx). К этому же классу относятся и конвертеры значений.
- Привязка команд осуществляется благодаря свойству элементов управления Command, она, так же как и привязка данных, может быть описана в XAML. При описании команд реализуют интерфейс ICommand (http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.input.icommand%28v=vs.105%29.aspx).
- Так как модель (данные), модель представления (подготовка данных) и представление (отображение данных в интерфейсе) разделены, при использовании шаблона MVVM особую значимость приобретает концепция использования так называемых данных периода проектирования (design-time data). Эти данные обычно представлены в некотором статичном виде, например – в файле, содержащем заранее заданный набор данных. Использование данных периода проектирования позволяет заниматься проектированием интерфейса приложения в виде, максимально приближенном к рабочему виду приложения.
Рассмотрим реализацию некоторых вышеописанных принципов в приложении, построенном по шаблону Приложение Windows Phone с привязкой к данным.