Windows Dev Center

Utilisation du modèle MVVM (Model-View-ViewModel) dans Hilo (applications du Windows Store en C++ et XAML)

Source : Développement d’une application du Windows Store en C++ et XAML de bout en bout : Hilo

Logo de Patterns & Practices

Page précédente | Page suivante

Relativement tôt dans le projet, nous avons décidé d’adopter le modèle MVVM (Model-View-ViewModel) pour l’architecture de Hilo. Nous étions attirés par le fait que le modèle MVVM facilite la maintenance et les tests de votre application du Windows Store en C++ et XAML, notamment lorsqu’elle croît. Le modèle MVVM est relativement nouveau pour les applications C++.

Téléchargement

Télécharger l’exemple Hilo
Télécharger le manuel (PDF)

Après avoir téléchargé le code, consultez Prise en main de Hilo pour obtenir des instructions.

Vous apprendrez

  • Comment les applications du Windows Store peuvent tirer profit du modèle MVVM.
  • Techniques recommandées pour appliquer le modèle MVVM aux applications du Windows Store
  • Comment mapper les vues aux éléments d’interface utilisateur.
  • Comment partager des modèles de vue entre des vues.
  • Comment exécuter des commandes dans un modèle de vue.

S’applique à

  • Windows Runtime pour Windows 8
  • Extensions des composants Visual C++ (C++/CX)
  • XAML

Qu’est-ce que le modèle MVVM ?

MVVM est un modèle d’architecture. Il s’agit d’une spécialisation du modèle de présentation qui a été introduit par Martin Fowler. Il est également lié au modèle MVC (Modèle-Vue-Contrôleur) et au modèle MVP (Modèle Vue Présentateur) que vous connaissez peut-être déjà.

Une application qui utilise le modèle MVVM sépare la logique métier, l’interface utilisateur et le comportement de présentation.

  • Les modèles représentent l’état et les opérations des objets métier que votre application manipule. Par exemple, Hilo lit et modifie les fichiers image, si bien qu’il est judicieux que les types de données pour les fichiers image et les opérations sur les fichiers image fassent partie du modèle de Hilo.
  • Les vues contiennent des éléments d’interface utilisateur et incluent tout code qui implémente l’expérience utilisateur de l’application. Une vue définit la structure, la disposition et l’apparence de ce que l’utilisateur voit à l’écran. Des grilles, des pages, des boutons et des zones de texte sont des exemples d’éléments gérés par les objets vue.
  • Les modèles de vue encapsulent l’état, les actions et les opérations de l’application. Un modèle de vue sert de couche de découplage entre le modèle et la vue. Il fournit les données dans un format que la vue peut consommer et met à jour le modèle de manière à ce que la vue n’ait pas besoin d’interagir avec le modèle. Les modèles de vue répondent aux commandes et déclenchent des événements. Ils agissent également en tant que sources de données pour toutes les données que les vues affichent. Les modèles de vue sont élaborés spécifiquement pour prendre en charge une vue. Vous pouvez considérer un modèle de vue comme une application sans l’interface utilisateur correspondante. Dans les applications du Windows Store, vous pouvez lier de façon déclarative des vues à leurs modèles de vue correspondants.

Voici les relations qui existent entre une vue, un modèle de vue et un modèle.

Relations entre une vue, un modèle de vue et un modèle

[Haut]

Modèle MVVM dans Hilo

Dans Hilo, il existe une classe de vue par page de l’interface utilisateur. (Une page est une instance de la classe Windows::UI::Xaml::Controls::Page.) Chaque vue possède une classe de modèle de vue correspondante. Tous les modèles de vue dans Hilo partagent le modèle de domaine de l’application, qui est souvent appelé simplement « modèle ». Le modèle est composé de classes que les modèles de vue utilisent pour implémenter les fonctionnalités de l’application.

Remarque  Si vous voulez accéder directement à une présentation pas à pas du code de vue et de modèle de vue dans Hilo, voir Création de pages et navigation entre les pages dans Hilo.

La solution Visual Studio Hilo.sln inclut des dossiers de solution nommés pour chacune des couches MVVM.

Dossiers de solution Visual Studio
  • Le dossier Models contient les fichiers .cpp (C++) et .h (en-tête C++) qui composent le modèle Hilo.
  • Le dossier Views contient les classes d’interface utilisateur et les fichiers XAML.
  • Le dossier ViewModels contient les fichiers .cpp et .h des classes de modèle de vue de l’application.

Avec le modèle MVVM, la liaison de données XAML laisse un modèle de vue agir comme le contexte de données d’une page. Un contexte de données est chargé de fournir des propriétés qui fournissent à la vue les données pour les éléments d’interface utilisateur dans la page.

Remarque  Vous n’êtes pas tenu d’utiliser une liaison de données pour connecter les vues et les modèles de vue. Il est possible également d’utiliser un fichier code-behind contenant du code C++ associé aux classes de page. Vous pouvez reconnaître les fichiers code-behind car ils utilisent le suffixe .xaml.cpp. Par exemple, dans Hilo, le fichier MainHubView.xaml.cpp est le fichier code-behind pour la page définie par le fichier MainHubView.xaml. De nombreux outils de conception visuelle, tels que Microsoft Expression, sont optimisés pour une utilisation avec la liaison de données.

Les modèles de vue se connectent au modèle sous-jacent de l’application en appelant des méthodes d’instance. Vous n’avez besoin d’aucune liaison spéciale pour effectuer ces appels. Si vous souhaitez une séparation forte entre le modèle et les modèles de vue de l’application, vous pouvez mettre en package les classes de modèle dans une bibliothèque distincte. Hilo n’utilise pas de bibliothèque distincte pour son modèle. À la place, il conserve simplement les fichiers qui définissent les classes de modèle dans un dossier distinct dans le projet Visual Studio Hilo.

[Haut]

Pourquoi utiliser le modèle MVVM pour Hilo ?

Il existe deux approches principales d’implémentation pour une interface utilisateur : l’utilisation d’un fichier code-behind pour la logique de présentation ou la séparation de la structure d’interface utilisateur et de la logique de présentation à l’aide d’un modèle tel que le modèle MVVM. Après avoir analysé nos besoins, nous avons choisi l’approche MVVM pour Hilo car :

  • Nous voulions tester notre logique de présentation. Le modèle MVVM permet de séparer proprement et aisément la logique de vue des contrôles d’interface utilisateur, ce qui est important pour l’automatisation de test.
  • Nous voulions nous assurer que les logiques de vue et de présentation pouvaient évoluer indépendamment et réduire les dépendances entre les développeurs et les concepteurs de l’expérience utilisateur. Le modèle MVVM, utilisé avec la liaison de données XAML, rend cela possible.

[Haut]

Informations supplémentaires

Vous trouverez plus d’informations sur le modèle MVVM en ligne. Voici quelques exemples en code managé, mais les concepts s’appliquent également à C++ :

[Haut]

Variations du modèle MVVM

Vous pouvez personnaliser le modèle MVVM de diverses manières. Examinons-en quelques-unes.

Mappage de vues à des éléments d’interface utilisateur autres que des pages

Dans Hilo, chaque classe de page est un objet vue MVVM et toutes les vues MVVM sont des pages. Toutefois, vous n’êtes pas tenu de faire la même chose. Par exemple, une vue pourrait être un DataTemplate pour un objet dans un ItemsControl.

Partage de modèles de vue entre plusieurs vues

Une vue peut posséder son propre modèle de vue ou partager celui d’une autre vue. Le choix dépend du fait que les vues partagent ou non un grand nombre de fonctionnalités communes. Dans Hilo, chaque vue est associée à un modèle de vue unique par souci de simplicité.

Exécution de commandes dans un modèle de vue

Vous pouvez utiliser la liaison de données pour les boutons et les autres contrôles d’interface utilisateur qui entraînent l’application à effectuer des opérations. Si le contrôle est une source de commande, la propriété Command du contrôle est liée par les données à une propriété ICommand sur le modèle de vue. Lorsque la commande du contrôle est appelée, le code présent dans le modèle de vue est exécuté. Nous vous recommandons d’utiliser la liaison de données pour les commandes lorsque vous utilisez MVVM.

Voici un exemple d’exécution d’une commande issu de Hilo. La page de rotation d’image contient un élément d’interface utilisateur pour le bouton Enregistrer le fichier. Ce code XAML provient du fichier RotateImageView.xaml.


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


L’expression "Command={Binding SaveCommand}" crée une liaison entre la propriété Command du bouton et la propriété SaveCommand de la classe RotateImageViewModel. La propriété SaveCommand contient un handle d’objet ICommand. Le code suivant provient du fichier RotateImageViewModel.cpp.


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


La variable membre m_saveCommand est initialisée dans le constructeur de la classe RotateImageViewModel. Le code issu du fichier RotateImageViewModel.cpp est le suivant :


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


La classe DelegateCommand de Hilo est une implémentation de l’interface ICommand. La classe définit le type délégué ExecuteDelegate. Les délégués vous permettent d’utiliser un pointeur vers une fonction membre C++ en tant qu’objet Windows Runtime pouvant être appelé. Hilo appelle le ExecuteDelegate lorsque l’interface utilisateur déclenche la commande.

Remarque  Pour plus d’informations sur l’extension de langage des délégués dans C++/CX, voir Délégués (C++/CX).

Comme nous avons utilisé la liaison de données, la modification de l’action de la commande d’enregistrement consiste à attribuer un autre objet DelegateCommand à la variable membre m_saveCommand. Il n’est pas nécessaire de modifier le fichier XAML de la vue.

Dans cet exemple, la fonction membre sous-jacente provient du fichier RotateImageViewModel.cpp.


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

Utilisation d’un objet localisateur de modèle de vue pour lier des vues à des modèles de vue

Dans Hilo, chaque vue (page) possède un modèle de vue correspondant.

Si vous utilisez le modèle MVVM, l’application a besoin de connecter ses vues à ses modèles de vue. Cela signifie que chaque vue doit posséder un modèle de vue attribué à sa propriété DataContext. Pour Hilo, nous avons utilisé une classe ViewModelLocator unique car nous avions besoin que le code configuré s’exécute avant que les éléments d’interface utilisateur soient liés au modèle de vue. La classe ViewModelLocator a des propriétés qui extraient un objet de modèle de vue pour chaque page de l’application. Reportez-vous à Création de pages et navigation entre les pages pour obtenir une description de la manière dont la classe ViewModelLocator lie les vues et les modèles de vue dans Hilo.

Vous n’êtes pas tenu d’utiliser une classe de localisateur de modèle de vue. En fait, il existe plusieurs manières de lier une vue à son objet de modèle de vue correspondant. Si vous n’utilisez pas une classe de localisateur de modèle de vue, vous pouvez connecter la création et la destruction des instances de modèle de vue à la durée de vie de l’objet vue correspondant. Par exemple, vous pouvez créer une nouvelle instance de modèle de vue chaque fois que la page est chargée.

Vous pouvez également connecter des vues à des modèles de vue dans un fichier code-behind. Le code d’un fichier code-behind peut instancier une nouvelle instance de modèle de vue et l’attribuer à la propriété DataContext de la vue. Vous pouvez instancier le modèle de vue dans la méthode Initialize de la page ou dans sa méthode OnNavigatedTo.

[Haut]

Conseils pour la conception d’applications du Windows Store à l’aide du modèle MVVM

Voici quelques conseils pour appliquer le modèle MVVM aux applications du Windows Store en C++ :

Conserver les dépendances de vue en dehors du modèle de vue

Lors de la conception d’une application du Windows Store à avec le modèle MVVM, vous devez décider de ce qui est placé dans le modèle, dans les vues et dans les modèles de vue. Cette division est souvent une affaire de goûts, mais quelques principes généraux s’appliquent. Dans l’idéal, vous définissez la vue à l’aide d’un code XAML, avec uniquement un code-behind limité qui ne contient pas la logique métier. Nous vous recommandons également de maintenir le modèle de vue sans dépendances sur les types de données pour les éléments d’interface utilisateur ou les vues. N’incluez pas de fichiers d’en-tête de vue dans les fichiers sources du modèle de vue.

Centraliser les conversions de données dans le modèle de vue ou une couche de conversion

Le modèle de vue fournit des données provenant du modèle sous une forme que la vue peut facilement utiliser. Pour cela, le modèle de vue doit parfois effectuer une conversion de données. Il est judicieux de placer cette conversion de données dans le modèle de vue car cela fournit des propriétés sous une forme à laquelle l’interface utilisateur peut se lier.

Il est également possible de disposer d’une couche de conversion de données distincte, située entre le modèle de vue et la vue. Cela peut avoir lieu, par exemple, lorsque les types de données ont besoin d’une mise en forme spéciale que le modèle de vue ne fournit pas.

Exposer les modes opérationnels dans le modèle de vue

Le modèle de vue peut également être chargé de définir les changements d’état logique qui affectent certains aspects de l’affichage dans la vue, tels qu’une indication qu’une opération est en attente ou qu’une commande particulière est disponible. Vous n’avez pas besoin que le code-behind active ou désactive des éléments d’interface utilisateur ; vous pouvez réaliser cela en créant une liaison avec une propriété de modèle de vue.

Voici un exemple.


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


Dans cet exemple, l’attribut IsTapEnabled est lié à la propriété HasPhoto du modèle de vue.

Garantir que les modèles de vue possèdent l’attribut Bindable

Pour que le modèle de vue puisse prendre part à la liaison de données avec la vue, les classes de modèle de vue doivent posséder l’attribut Windows::UI::Xaml::Data::Bindable pour garantir que le type est inclus dans le fichier généré de langage XAML.

En outre, vous devez également inclure l’en-tête pour votre modèle de vue dans un fichier d’en-tête App.xaml.h, directement ou indirectement. Dans Hilo, tous les fichiers d’en-tête des modèles de vue sont inclus dans le fichier ViewModelLocator.h, qui est inclus dans le fichier App.xaml.h. Cela garantit que les types nécessaires pour travailler avec le langage XAML sont générés correctement au moment de la compilation.

Remarque  Pour plus d’informations sur l’extension de langage des attributs du langage C++/CX, voir Attributs définis par l’utilisateur (C++/CX).

Voici un exemple de l’attribut Bindable.


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

Garantir que les modèles de vue implémentent l’interface INotifyProperyChanged pour que la liaison de données fonctionne

Les modèles de vue qui ont besoin de notifier les clients qu’une valeur de propriété a changé doivent déclencher l’événement PropertyChanged. Pour cela, les classes de modèle de vue ont besoin d’implémenter l’interface Windows::UI::Xaml::Data::INotifyPropertyChanged. Windows Runtime inscrit un gestionnaire pour cet événement lorsque l’application s’exécute. Visual Studio fournit une implémentation de l’interface INotifyPropertyChanged dans la classe de modèle BindableBase que vous pouvez utiliser comme classe de base pour toute source de données XAML. Voici le fichier d’en-tête généré, issue de 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);
};	  

L’implémentation générée appelle le gestionnaire lorsque l’événement est déclenché.


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

Remarque  C++/CX inclut les événements et les propriétés dans le cadre du langage de programmation. Il inclut les mots-clés event et property. Les types Windows Runtime déclarent les événements dans leurs interfaces publiques, auxquelles votre application peut s’abonner. L’abonné effectue des actions personnalisées lorsque l’éditeur déclenche l’événement. Pour plus d’informations sur les fonctionnalités C++/CX qui prennent en charge Windows Runtime, voir Création de composants Windows Runtime en C++. Pour plus d’informations sur l’extension de langage d’événement de C++/CX qui est utilisée dans cet exemple de code, voir Événements (C++/CX).

Les classes de modèle de vue peuvent hériter de l’implémentation INotifyPropertyChanged en dérivant de la classe BindableBase. Par exemple, vous trouverez ci-dessous la déclaration de la classe ViewModelBase dans Hilo. Ce code provient du fichier ViewModelBase.h.


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

Chaque fois que les modèles de vue ont besoin d’indiquer à l’interface utilisateur qu’une propriété liée a changé, ils appellent la méthode OnPropertyChanged dont ils ont hérité à partir de la classe BindableBase. Par exemple, ce qui suit est une méthode set de propriété définie dans la classe 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");
}


Remarque  Les notifications de propriété au code XAML doivent survenir dans le thread d’interface utilisateur. Cela signifie que la méthode OnPropertyChanged et tous ses appelants doivent également survenir dans le thread d’interface utilisateur de l’application. En règle générale, une convention utile précise que toutes les propriétés et méthodes de modèle de vue doivent être appelées dans le thread d’interface utilisateur de l’application.

Maintenir indépendants les vues et les modèles de vue

Si vous suivez les principes énoncés ici, vous serez en mesure de réimplémenter un modèle de vue sans aucune modification de la vue. La liaison de vues à une propriété particulière dans sa source de données doit être une dépendance principale de la vue sur son modèle de vue correspondant. (Si vous renommez une propriété liée dans le modèle de vue, vous devez la renommer également dans l’expression de liaison de données XAML.)

Utiliser des techniques de programmation asynchrones pour maintenir l’interface utilisateur réactive

Les applications du Windows Store se rapportent à une expérience utilisateur rapide et fluide. Pour cette raison, Hilo maintient le thread d’interface utilisateur non bloqué. Hilo utilise des méthodes de bibliothèque asynchrones pour les opérations d’E/S et les tâches parallèles lorsque les opérations effectuent une quantité importante de calculs. Hilo déclenche des événements pour notifier de façon asynchrone la vue au sujet d’une modification de propriété.

Voir Programmation asynchrone pour les applications du Windows Store en C++ et XAML pour plus d’informations.

Toujours observer les règles de thread pour les objets Windows Runtime

Les objets qui sont créés par des appels dans Windows Runtime sont parfois à thread unique. Cela signifie que vous devez appeler les méthodes, les propriétés et les gestionnaires d’événements à partir du contexte de thread qui a été utilisé pour créer l’objet. Dans la plupart des cas, le contexte représente le thread d’interface utilisateur de l’application.

Pour éviter les erreurs, l’application Hilo a été conçue de sorte que les appels dans ses modèles de vue surviennent sur le thread d’interface utilisateur de l’application. (Les classes de modèle effectuent des opérations longues, telles que le traitement d’image sur les threads de travail.)

Voir Modèles de programmation pour interface utilisateur asynchrone pour plus d’informations. Voir Contrôle du thread d’exécution pour plus d’informations sur le modèle de thread qu’utilisent les applications du Windows Store.

Pour une présentation pas à pas de l’utilisation par Hilo de la programmation asynchrone, voir Programmation asynchrone pour les applications du Windows Store en C++ et XAML.

[Haut]

 

 

Afficher:
© 2015 Microsoft