Применение паттерна MVVM как оптимального при проектировании WPF и Silverlight приложений
Цель лекции: рассмотреть ключевые классы паттерна MVVM: вид, модель представления, модель, а так же их функциональное взаимодействие. Разобрать интерфейсы, ответственные за осуществление привязки данных: INotifyPropertyChanged. и INotifyCollectionChanged. Узнать о различных способах реализации команд в паттерне MVVM. Познакомить с интерфейсом IDataErrorInfo. Дать обзор различных возможностей определять зависимости между представлением, моделью представления, и классами модели.
Паттерн Model-View-ViewModel (MVVM)
MVVM поможет вам разделить бизнес-логику и логику представления приложения от его пользовательского интерфейса (UI). Поддержание разделения между логикой приложения и UI помогает решать проблемы разработки и проектирования и может сделать ваше приложение намного более лёгким для тестирования, поддержания, и развития. Это может также значительно улучшить возможности повторного использования кода и позволяет разработчикам и дизайнерам UI более легко сотрудничать при разработке соответствующих частей приложения.
Используя паттерн MVVM, UI приложения, нижележащее представление и бизнес-логика разделяются на три отдельных класса: вид, который инкапсулирует UI и его логику; модель представления, которая инкапсулирует логику представления и состояния; и модель, которая инкапсулирует бизнес-логику приложения и данные.
Обязанности и характеристики классов
Паттерн MVVM является близкой разновидностью паттерна Presentation Model, оптимизированного для усиления некоторых базовых возможностей WPF и Silverlight, таких как привязка данных, шаблоны данных, команды, и поведения.
В паттерне MVVM представление инкапсулирует UI и любую логику UI, модель представления инкапсулирует логику представления и состояния, и модель инкапсулирует бизнес-логику и данные. Представление взаимодействует с моделью представления посредством привязки данных, команд и событий уведомления об изменениях. Запросы модели представления, наблюдатели и координаты обновляют модель. Преобразования, валидация и агрегация данных необходимы для отображения данных в представлении.
На рисунке 13.1 показаны три класса MVVM и их взаимодействие.
Класс представления (View)
Ответственность представления состоит в том, чтобы определить структуру и появление того, что пользователь видит на экране. В идеале, code-behind (фоновый код) представления содержит только конструктор, который вызывает метод InitializeComponent. В некоторых случаях, code-behind может содержать код логики UI, который реализует визуальное поведение, являющееся трудным или неэффективным для выражения в XAML, такое как сложные анимации, или когда код должен непосредственно управлять визуальными элементами, являющимися частью представления. Недопустимо помещать код логики, нуждающийся в тестировании, в представление. Как правило, код логики в code-behind представления может быть протестирован через автоматизацию UI.
В Silverlight и WPF, выражения привязки данных в представлении вычисляются по отношению к его контексту данных. В MVVM модель представления устанавливается в качестве контекста данных представления. Модель представления реализует свойства и команды, к которым представление может быть привязано и уведомляет представление о любых изменениях состояния через события уведомления об изменениях. Обычно есть непосредственное отношение между представлением и его моделью представления.
Как правило, представления наследуются от классов Control или UserControl. Однако, в некоторых случаях, представление может быть представлено шаблоном данных, который определяет элементы UI, которые будут использоваться, чтобы визуально представить объект. Используя шаблоны данных, разработчик может легко задать, как модель представления будет представлена, или может изменить её визуальное представление по умолчанию, не изменяя базовый объект или его поведение непосредственно.
Подводя итоги, у представления есть следующие ключевые характеристики:
- Представление является визуальным элементом, таким как окно, страница, пользовательский элемент управления или шаблон данных. Представление определяет элементы управления, их компоновку и стиль.
- Представление ссылается на модель представления через свое свойство DataContext. Элементы управления в представлении привязаны к свойствам и командам модели представления.
- Представление может настроить поведение привязки данных между представлением и моделью представления. Например, представление может использовать преобразователи значений, чтобы отформатировать данные, которые будут показаны в UI, или использовать правила валидации, чтобы предоставить дополнительную проверку вводимых пользователем данных.
- Представление задаёт и обрабатывает визуальное поведение UI, такое как анимации или переходы, которые могут быть инициированы изменением состояния модели представления или через взаимодействие пользователя с UI.
- Code-behind представления может определить логику UI, чтобы реализовать визуальное поведение, которое трудно выразить в XAML, или которое требует прямых ссылок на определенные элементы управления UI, определенные в представлении.
Класс модели представления (View Model)
Модель представления в паттерне MVVM инкапсулирует логику представления и данные для отображения. У него нет никаких прямых ссылок на представление или любое знание о реализации или типе представления. Модель представления реализует свойства и команды, к которым представление может привязать данные и уведомляет представление о любых изменениях состояния через события уведомления. Свойства и команды, которые предоставляет модель представления, определяют функциональность, полагающуюся UI, но представление определяет, как эта функциональность должна будет представлена.
Как правило, модель представления определяет команды или действия, которые могут быть представлены в UI и вызваны пользователем. Типичный примером является то, когда модель представления предоставляет команду Submit, которая позволяет пользователю передать данные веб-сервису или репозитарию данных. Представление может представить эту команду как кнопку так, чтобы пользователь мог нажать её для передачи данных. Как правило, когда команда становится недоступной, ее связанное представление UI становится отключенным. Команды дают способ инкапсулировать пользовательские действия и чисто отделить их от их визуального представления в UI.
В итоге, модель представления имеет следующие ключевые характеристики:
- Модель представления является неотображаемым классом и не наследуется ни от какого базового класса WPF или Silverlight. Она инкапсулирует логику представления, необходимую для поддержки пользовательских действий в приложении. Модель представления является тестируемой независимо от представления и модели.
- Модель представления обычно непосредственно не ссылается на представление. Она реализует свойства и команды, к которыми представление может привязать данные. Она уведомляет представление о любых изменениях состояния через события уведомления через интерфейсы INotifyPropertyChanged и INotifyCollectionChanged.
- Модель представления координирует взаимодействие представления с моделью. Она может преобразовать или управлять данными так, чтобы они могли быть легко использованы представлением, и может реализовать дополнительные свойства, которые, возможно, не присутствуют в модели. Она может также реализовать валидацию данных через интерфейсы IDataErrorInfo или INotifyDataErrorInfo.
- Модель представления может определить логические состояния, которые представление может визуально представить пользователю.
Представление или Модель Представления?
Часто определение того, где должна быть реализована определенная функциональность, не очевидно. Общее правило гласит: Что-либо касающееся определенного визуального отображения UI на экране и что может быть модернизировано позже (даже если вы в настоящий момент не планируете модернизировать это), должно войти в представление; что-либо, что важно для логического поведения приложения, должно войти в модель представления. Кроме того, так как у модели представления не должно быть никаких явно заданных знаний об определенных визуальных элементах в представлении, код, чтобы программно управлять визуальными элементами в пределах представления должен находиться в code-behind представления или инкапсулироваться в поведении. Точно так же код для получения или управления элементами данных, которые должны быть показаны в представлении посредством привязки данных, должен находиться в модели представления. Например, цвет выделения выбранного пункта в поле списка должен быть определен в представлении, но список элементов для отображения, и ссылка на выбранный пункт непосредственно, должны быть определены моделью представления.
Класс модели (Model)
Модель в паттерне MVVM инкапсулирует бизнес-логику и данные. Бизнес-логика определяется как любая логика приложения, которая касается извлечения и управления данными приложения и для того, чтобы удостовериться, что налагаются бизнес-правила, которые гарантируют непротиворечивость и валидность данных. Чтобы максимизировать возможности повторного использования, модели не должны содержать никакой специфичной для отображения или использования логики или поведения.
Как правило, модель реализует средства, которые облегчают привязку к представлению. Это обычно означает, что поддерживаются уведомления об изменениях свойств или коллекций через интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. Классы моделей, которые предоставляют наборы объектов, обычно наследуются от класса ObservableCollection<T>, который обеспечивает реализацию интерфейса INotifyCollectionChanged. Модель может также поддерживать валидацию данных и сообщение об ошибках через интерфейсы IDataErrorInfo (или INotifyDataErrorInfo). Эти интерфейсы позволяют уведомить WPF и Silverlight, когда значения привязанных данных изменяются для обновления UI. Они также разрешают поддержку валидации данных и сообщения об ошибках в уровне UI.
У модели есть следующие ключевые характеристики:
- Классы модели являются не визуальными классами, которые инкапсулируют данные приложения и бизнес-логику. Они ответственны за управление данными приложения и за обеспечение их непротиворечивости и валидности, инкапсулируя необходимые бизнес-правила и логику подтверждения правильности данных.
- Классы модели непосредственно не ссылаются на классы представления или модели представления и не имеют никакой зависимости от того, как эти классы реализуются.
- Классы модели обычно предоставляют уведомления об изменении свойств или коллекций через интерфейсы INotifyPropertyChanged и INotifyCollectionChanged. Это позволяет им быть легко привязанными к представлению. Классы модели, которые представляют коллекции объектов, обычно наследуются от класса ObservableCollection <T>.
- Классы модели обычно обеспечивают валидацию данных и сообщение об ошибках через интерфейсы IDataErrorInfo или INotifyDataErrorInfo.
- Классы модели обычно используются вместе со службой или репозитарием, который инкапсулирует доступ к данным и кэширование.
Взаимодействие классов
Паттерн MVVM обеспечивает чистое разделение между пользовательским интерфейсом вашего приложения, его логикой представления, и его бизнес-логикой и данными, разделяя каждый элемент на отдельные классы. Поэтому, когда вы реализуете MVVM, важно разделить код вашего приложения на корректные классы. Хорошо разработанные классы представления, модели представления, и модели будут не только инкапсулировать корректный тип кода и поведения; они также будут разработаны так, чтобы легко взаимодействовать с друг другом через привязку данных, команды и интерфейсы подтверждения правильности данных.
Взаимодействие между представлением и его моделью представления являются, возможно, самыми важными для рассмотрения, но взаимодействия между классами модели и модели представления также важны. Далее в лекции описываются различные образцы этих взаимодействий и описывают, как их разрабатывать, реализовывая образец MVVM в ваших приложениях.