Использование шаблона "модель — представление — модель представления" (MVVM) в приложении Hilo (приложения Магазина Windows на C++ и XAML)

От: Полный цикл разработки приложения Магазина Windows на C++ и XAML: приложение Hilo

Логотип Patterns and Practices

Предыдущая страница | Следующая страница

Ранее в рамках проекта мы решили приспособить шаблон Model-View-ViewModel (MVVM) для архитектуры приложения Hilo. Привлекательным показался тот факт, что шаблон MVVM облегчает обслуживание и тестирование приложения Магазина Windows на C++ и XAML, в особенности по мере его развития. MVVM является относительно новым шаблоном для приложений на C++.

Скачать

Скачать образец Hilo
Скачать книгу (в формате PDF)

Скачав код, ознакомьтесь с инструкциями в разделе Начало работы с Hilo.

Содержание раздела

  • Преимущества использования MVVM для универсальных приложений Windows.
  • Рекомендованные методики применения шаблона MVVM к приложениям Магазина Windows.
  • Привязка представлений к элементам пользовательского интерфейса.
  • Совместное использование моделей представления в различных представлениях.
  • Выполнение команд в модели представления.

Применимо к:

  • среде выполнения Windows в Windows 8
  • расширениям компонентов Visual C++ (C++/CX)
  • XAML

Что такое MVVM?

MVVM является архитектурным шаблоном. Он представляет собой специализацию шаблона модели представления, внедренного Мартином Фоулером. Он также имеет отношение к шаблонам Model-View-Controller (MVC) и Model-View-Presenter (MVP), которые могут быть вам знакомы.

Приложение, использующее MVVM, разделяет бизнес-логику, пользовательский интерфейс и поведение презентации.

  • Модели представляют состояние и операции бизнес-объектов, с которыми работает ваше приложение. Например, приложение Hilo считывает и изменяет файлы изображений, поэтому целесообразно включить в модель Hilo типы данных для файлов изображений и операции с файлами изображений.
  • Представления содержат элементы пользовательского интерфейса, а также включают любой код, реализующий взаимодействие приложения с пользователем. Представление определяет структуру, расположение и оформление элементов, отображаемых для пользователя на экране. Сетки, страницы, кнопки и текстовые поля являются примерами элементов, которыми управляют объекты представления.
  • Модели представления объединяют состояние, действия и операции приложения. Модель представления служит уровнем отделения модели от представления. Она поставляет данные в удобном для представления формате и обновляет модель, что избавляет представление от необходимости взаимодействия с моделью. Модели представления реагируют на команды и запускают события. Они также выступают в качестве источников данных для любых отображаемых представлениями данных. Модели представления строятся специально для поддержки определенного представления. Модель представления можно воспринимать как приложение без пользовательского интерфейса. В приложениях Магазина Windows имеется возможность декларативной привязки представлений к соответствующим моделям представления.

Ниже представлены отношения между представлением, моделью представления и моделью.

Отношения между представлением, моделью представления и моделью

[Наверх]

MVVM в приложении Hilo

В приложении Hilo один класс элементов управления веб-интерфейса соответствует одной странице пользовательского интерфейса. (Страница является экземпляром класса Windows::UI::Xaml::Controls::Page.) Каждому представлению соответствует определенный класс модели представления. Все модели представления в приложении Hilo совместно используют доменную модель приложения, которую зачастую называют просто моделью. Данная модель состоит из классов, используемых моделями представления для реализации функциональности приложения.

Примечание  Если вы желаете перейти непосредственно к пошаговому руководству по коду, описывающему представления и модели представления для Hilo, см. раздел Создание страниц и навигация в приложении Hilo.

В решении Visual Studio Hilo.sln имеются папки, именованные в соответствии с каждым уровнем MVVM.

Папки решений Visual Studio
  • Папка Models содержит файлы .cpp (C++) и .h (заголовок C++), составляющие модель приложения Hilo.
  • Папка Views содержит классы пользовательского интерфейса и файлы XAML.
  • Папка ViewModels содержит файлы .cpp и .h для классов модели представления приложения.

С использованием шаблона MVVM привязка данных XAML позволяет модели представления выступать в качестве контекста данных определенной страницы. Контекст данных отвечает за обеспечение свойств, поставляющих представлению данные для элементов пользовательского интерфейса на этой странице.

Примечание  Для связи между представлениями и моделями представлений использование привязки данных не требуется. Также можно использовать файл с выделенным кодом, содержащий код C++ и связанный с классами страницы. Файлы с выделенным кодом распознаются по суффиксу .xaml.cpp. Например, в приложении Hilo файл MainHubView.xaml.cpp является файлом с выделенным кодом для страницы, определяемой файлом MainHubView.xaml. Многие визуальные инструменты проектирования, такие как Microsoft Expression, оптимизированы для работы с привязкой данных.

Модели представления связываются с базовой моделью приложения путем вызова экземплярных методов. Данные вызовы не требуют особой привязки. Для однозначного разделения модели и моделей представления приложения можно запаковать классы модели в отдельную библиотеку. Приложение Hilo для своей модели отдельную библиотеку не использует. Вместо этого файлы, определяющие классы модели, хранятся в отдельной папке в проекте Visual Studio Hilo.

[Наверх]

Для чего нужен MVVM в Hilo?

Существуют два основных подхода к реализации пользовательского интерфейса: использование файла с выделенным кодом для логики представления или разделение структуры пользовательского интерфейса и логики представления при помощи некоего шаблона, например шаблона MVVM. Проанализировав свои потребности, мы выбрали для приложения Hilo подход с использованием MVVM, поскольку:

  • Требовалось провести тестирование логики презентации. MVVM облегчает четкое разделение логики представления и элементов управления пользовательского интерфейса, что важно для автоматизации тестирования.
  • Требовалось обеспечить независимое развитие логики представления и презентации, а также уменьшить зависимости между дизайнерами и разработчиками взаимодействия с пользователем. MVVM, используемый с привязкой данных XAML, обеспечивает такую возможность.

[Наверх]

Дополнительные сведения

Дополнительные сведения о MVVM доступны в сети Интернет. Ниже приведен ряд примеров в управляемом коде, но концепции применимы и к C++:

[Наверх]

Варианты шаблона MVVM

Настройка шаблона MVVM возможна несколькими способами. Рассмотрим некоторые из них.

Привязка представлений к элементам пользовательского интерфейса, кроме страниц

В приложении Hilo каждый класс страницы является объектом представления MVVM, а все представления MVVM являются страницами. Но одинаковых действий они не требуют. Например, представление может быть DataTemplate для объекта в ItemsControl.

Совместное использование моделей представления несколькими представлениями

Представление может иметь собственную модель представления или использовать модель другого представления. Выбор зависит от наличия или отсутствия у представлений большого количества общих функций. Для упрощения работы в приложении Hilo каждое представление связано с уникальной моделью представления.

Выполнение команд в модели представления

Можно использовать привязку данных для кнопок и других элементов управления пользовательского интерфейса, вызывающих выполнение приложением определенных операций. Если элемент управления является источником команд, его свойство Command привязывается к данным свойства ICommand модели представления. При вызове команды элемента управления выполняется код в модели представления. При использовании MVVM для команд рекомендуется применение привязки к данным.

Ниже приведен пример выполнения команды из приложения Hilo. Страница поворота изображения содержит элемент пользовательского интерфейса для кнопки "Сохранение файла". Данный код XAML поступает из файла RotateImageView.xaml.


<Button x:Name="SaveButton"
        x:Uid="AcceptAppBarButton"
        Command="{Binding SaveCommand}" 
        Style="{StaticResource AcceptAppBarButtonStyle}"
        Tag="Save" />


Выражение "Command={Binding SaveCommand}" создает привязку между свойством Command кнопки и свойством SaveCommand класса RotateImageViewModel. Свойство SaveCommand возвращает дескриптор объекту ICommand. Следующий код поступает из файла RotateImageViewModel.cpp.


ICommand^ RotateImageViewModel::SaveCommand::get()
{
    return m_saveCommand;
}


Переменная члена m_saveCommand инициализируется в конструкторе класса RotateImageViewModel. Ниже приведен код из файла RotateImageViewModel.cpp.


m_saveCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &RotateImageViewModel::SaveImage), nullptr);


Класс DelegateCommand Hilo является реализацией интерфейса ICommand. Данный класс определяет тип делегата ExecuteDelegate. Делегаты позволяют использовать указатель на функцию члена C++ в качестве вызываемого объекта среды выполнения Windows. Hilo вызывает ExecuteDelegate при запуске команды интерфейсом пользователя.

Примечание  Дополнительные сведения о расширении языка делегатов в C++/CX см. в разделе Делегаты (C++/CX).

Так как используется привязка данных, изменение действия команды сохранения является вопросом назначения другого объекта DelegateCommand переменной члена m_saveCommand. Изменять файл XAML представления не требуется.

В приведенном примере функция базового члена поступает из файла RotateImageViewModel.cpp.


void RotateImageViewModel::SaveImage(Object^ parameter)
{
   // Asynchronously save image file
}	  

Использование объекта локатора модели представления для привязки представлений к моделям представления

В приложении Hilo каждому представлению (странице) соответствует определенная модель представления.

При использовании MVVM приложение должно связать свои представления с соответствующими моделями представления. Это означает, что каждое представление должно иметь модель представления, назначенную его свойству DataContext. Для приложения Hilo используется один класс ViewModelLocator, так как мы настроили код на выполнение до привязки элементов пользовательского интерфейса к модели представления. Класс ViewModelLocator имеет свойства, возвращающие объект модели представления для каждой страницы приложения. См. раздел Создание и навигация между страницами для ознакомления с описанием процесса привязки классом ViewModelLocator представлений и моделей представления в приложении Hilo.

Использование класса локатора модели представления не требуется. Фактически существует несколько способов привязки представления к соответствующему объекту модели представления. Если класс локатора модели представления не используется, можно связать создание и уничтожение экземпляров модели представления со временем существования соответствующего объекта представления. Например, можно создавать новый экземпляр модели представления при каждой загрузке страницы.

Также можно связать представления и модели представления в файле с выделенным кодом. Код в файле с выделенным кодом может создавать новый экземпляр модели представления и назначать его свойству DataContext представления. Можно создавать экземпляр модели представления в методах Initialize или OnNavigatedTo страницы.

[Наверх]

Советы по разработке универсальных приложений Windows с использованием MVVM

Ниже приведен ряд советов по применению шаблона MVVM к приложениям Магазина Windows на C++.

Не включайте зависимости представления в модель представления

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

Централизация преобразований данных в модели представления или в слое преобразования.

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

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

Вывод режимов работы в модели представления

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

Пример


<Grid Background="{Binding HasPhotos, Converter={StaticResource BrushConverter}}"
      Height="150"
      IsTapEnabled="{Binding HasPhotos}"
      PointerEntered="OnZoomedOutGridPointerEntered"
      Margin="0"
      Width="150">


В данном примере атрибут IsTapEnabled привязан к свойству HasPhoto модели представления.

Наличие атрибута привязки в моделях представления

Для участия модели представления в привязке данных к представлению классы модели представления должны иметь атрибут Windows::UI::Xaml::Data::Bindable, обеспечивающий включение соответствующего типа в файл, создаваемый XAML.

Кроме того, необходимо напрямую либо опосредованно включить заголовок модели представления в файл заголовка App.xaml.h. В приложении Hilo все файлы заголовков моделей представления включаются в файл ViewModelLocator.h, который включен в файл App.xaml.h. Это обеспечивает соответствующее создание типов, необходимых для работы с XAML, во время компиляции.

Примечание  Дополнительные сведения по расширению языка атрибутов C++/CX см. в разделе Пользовательские атрибуты (C++/CX).

Ниже приведен пример атрибута Bindable.


[Windows::UI::Xaml::Data::Bindable] 
[Windows::Foundation::Metadata::WebHostHiddenAttribute]
public ref class MainHubViewModel sealed : public ViewModelBase 
{ 
  // ...  
} 	  

Реализация моделями представления интерфейса INotifyProperyChanged, обеспечивающего выполнение привязки данных

Модели представления, уведомляющие клиентов об изменении значения свойства, должны изменять событие PropertyChanged. Для этого классы модели представления должны реализовывать интерфейс Windows::UI::Xaml::Data::INotifyPropertyChanged. Среда выполнения Windows регистрирует обработчик данного события при запуске приложения. Visual Studio обеспечивает реализацию интерфейса INotifyPropertyChanged в классе шаблона BindableBase, который можно использовать как базовый класс для любого источника данных XAML. Ниже приведен созданный файл заголовка из BindableBase.h.



[Windows::Foundation::Metadata::WebHostHidden]
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
{
  public:
    virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;

   // ...

  protected:
    virtual void OnPropertyChanged(Platform::String^ propertyName);
};	  

Созданная реализация вызывает обработчик при изменении указанного события.


void BindableBase::OnPropertyChanged(String^ propertyName)
{
    PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}	  

Примечание  C++/CX включает события и свойства как часть языка программирования. В этом языке имеются ключевые слова для события и свойства. Типы среды выполнения Windows объявляют события в своих открытых интерфейсах, на которые может подписаться ваше приложение. Подписчик выполняет настраиваемые действия при запуске события издателем. Дополнительные сведения о компонентах C++/CX, поддерживающих среду выполнения Windows, см. в разделе Создание компонентов среды выполнения Windows на C++. Дополнительные сведения по расширению языка событий C++/CX, приведенному в примере кода, см. в разделе События (C++/CX).

Классы модели представления могут наследовать реализацию INotifyPropertyChanged из класса BindableBase. Например, декларация класса ViewModelBase в приложении Hilo. Следующий код поступает из файла ViewModelBase.h.


public ref class ViewModelBase : public Common::BindableBase
{
  // ...
}	  

Когда модели представления должны сообщить интерфейсу пользователя об изменении свойства привязки, они вызывают метод OnPropertyChanged, унаследованный от класса BindableBase. Например, метод набора свойств, определенный в классе RotateImageViewModel.


void RotateImageViewModel::RotationAngle::set(float64 value)
{
    m_rotationAngle = value;

    // Derive margin so that rotated image is always fully shown on screen.
    Thickness margin(0.0);
    switch (safe_cast<unsigned int>(m_rotationAngle))
    {
    case 90:
    case 270:
        margin.Top = 110.0;
        margin.Bottom = 110.0;
        break;
    }
    m_imageMargin = margin;
    OnPropertyChanged("ImageMargin");
    OnPropertyChanged("RotationAngle");
}


Примечание  Уведомления свойств для XAML должны содержаться в потоке пользовательского интерфейса. Это означает, что метод OnPropertyChanged и все его абоненты также должны содержаться в потоке интерфейса пользователя приложения. В целом можно сделать вывод, что все методы и свойства модели представления должны вызываться в потоке пользовательского интерфейса приложения.

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

Следуя обозначенным в настоящем разделе принципам, вы сможете выполнять повторную реализацию модели представления без каких-либо изменений представления. Привязка представлений к определенному свойству в его источнике данных должна быть зависимостью субъекта представления от соответствующей модели представления. (При переименовании привязанного свойства в модели представления необходимо переименовать его и в выражении привязки данных XAML.)

Использование методик асинхронного программирования для обеспечения высокой скорости отклика пользовательского интерфейса

Приложения Магазина Windows обеспечивают быстрое и гибкое взаимодействие с пользователем. По этой причине в приложении Hilo поток пользовательского интерфейса остается разблокированным. Приложение Hilo использует методы асинхронной библиотеки для операций ввода-вывода и параллельных задач, когда выполнение операций подразумевает значительное количество вычислений. Приложение Hilo вызывает события для асинхронного уведомления представления об изменении свойства.

Подробнее: Асинхронное программирование для универсальных приложений Windows на C++ и XAML.

Соблюдайте потоковые правила для объектов среды выполнения Windows

Объекты, создаваемые в среде выполнения Windows вызовами, периодически бывают однопотоковыми. Это означает необходимость вызова методов, свойств и обработчиков событий из контекста потока, использовавшегося для создания объекта. В большинстве случаев данный контекст содержится в потоке пользовательского интерфейса приложения.

Во избежание ошибок в приложении Hilo вызовы моделей представления проходят в потоке пользовательского интерфейса приложения. (Классы модели выполняют длительные операции, например обработку изображений, в рабочих потоках.)

Подробнее см. в разделе о шаблонах программирования для асинхронного пользовательского интерфейса. Подробнее о потоковой модели, используемой универсальными приложениями Windows: Управление потоком исполнения.

Пошаговое руководство по применению асинхронного программирования в Hilo см. в разделе Асинхронное программирование для универсальных приложений Windows на C++ и XAML.

[Наверх]

 

 

Показ:
© 2015 Microsoft