Cet article est le résultat d’une réflexion sur un problème souvent rencontré avec le pattern MVVM et d’une réaction à l’article de Thomas Lebrun qui traite de ce sujet, mais pas avec le même contexte. En effet, Thomas Lebrun propose une solution sans framework d’injection de dépendances et en WPF, moi, je vais vous présenter ma solution avec Prism et Silverlight 3.
Commençons par poser le problème, votre ViewModel exécute une action vers un service (web…) qui déclenche une erreur, quel est la meilleure façon d’avertir l’utilisateur de cette erreur ?
Soit vos ViewModels se charge de l’affichage (Beurk !), soit vous créez un service en charge des erreurs. La deuxième solution s’impose naturellement.
Si vous souhaitez voir le résultat avant de continuer, vous pouvez trouver le résultat à la fin de l’article.
Architecture
Pour cette démo technique, je vais simplement créer une application Silverlight composée de deux modules, un qui aura une exception à gérer et l’autre qui posera une question à l’utilisateur.
Respectons le système de modularité de Prism, voici notre architecture :
Notre application sera composée d’une application Silverlight 3 et de 4 librairies.
PromptWithPrism.Infrastructure : Cette librairie sera composée de tout ce qui a vocation à être partagé entre les modules.
PromptWithPrism.Modules.Prompt : Ce module est en charge de l’affichage des prompts, que ce soit des erreurs ou des questions à afficher à l’utilisateur.
PromptWithPrism.Modules.Panic : Lui sera chargé simplement de simuler des erreurs.
PromptWithPrism.Modules.Question : Enfin, ce dernier module posera une question à l’utilisateur.
Comme vous avez pu le remarquer, cette architecture permet de ne pas avoir de liaison directe entre les modules ce qui facilite les tests et le développement en parallèle.
Commençons par définir le service qui sera en charge des prompts. Dans notre démo, nous nous limiterons à l’affichage d’exception et de MessageBoxChildWindow. Nous exposerons donc une interface de ce type :
Rien de bien compliqué jusqu’ici, une méthode permettant d’afficher une exception à l’utilisateur et une autre méthode permettant d’afficher une MessageBox et de spécifier un callback à appeler lorsque l’utilisateur a répondu.
De plus pour une question de souplesse, notre service interceptera tous les événements du type ShowErrorRequiredEvent ce qui permettra à tout endroit du code d’afficher une erreur et éviter un pattern Singleton pour le service de prompt.
Voilà notre service est plus ou moins défini, passons au code.
Tout d’abord, créons notre Shell Prism, pour ceux qui n’ont pas l’habitude de Prism, le «Shell» est en fait une interface composée de régions où seront affichés les modules. Si vous n’avez toujours pas compris, continuez à lire vous allez comprendre.
3 régions composent ce Shell, la première région “PromptContainer” est “au dessus” des autres, car elle aura pour rôle de contenir des fenêtres modales (ChildWindow). Enfin, les deux autres régions correspondent aux vues de nos deux modules.
Le module Prompt
Ce module est composé de deux parties :
Le service Prompt.
Le conteneur de fenêtre modale.
Commençons par ce dernier, en effet le conteneur est très simple, c’est une vue vide qui sera chargée de contenir les ChildWindow et ainsi les afficher.
Nous avons maintenant notre conteneur, passons au service. Nous travaillons avec Prism, donc avec Unity qui nous offre de l’injection de dépendances alors servons en nous !
Notre service a besoin d’afficher des fenêtres modales et d’intercepter l’événement ShowErrorRequiredEvent.
Hop là, le constructeur sera résolu par notre conteneur Unity que nous configurerons juste après. Implémentons maintenant notre interface IPromptService :
Voilà, nous avons un service capable d’afficher des prompts. Pour le code des fenêtres ErrorChildWindow & MessageBoxChildWindow, je vous laisse télécharger le code source à la fin de l’article.
Rien de bien compliqué pour les habitués de Prism, en revanche pour les autres ça ne doit pas être très clair.
Prism permet de construire une application sous forme de module comme je l’ai expliqué au début de cet article. Il commence par les initialiser un par un. C’est au moment de cette initialisation qu’un module doit enregistrer ces types au conteneur Unity et enregistrer ses vues aux régions du Shell. Une fois ceci fait, le conteneur Unity peut résoudre le constructeur d’une classe en piochant dans les objets qu’on lui a déclarés, c’est ce qu’on appelle de l’injection de dépendance. En effet, c’est le conteneur qui se charge d’injecter les classes nécessaires la construction d’une classe.
Les modules Panic et Question
Le reste de cet article montre l’utilisation de notre service avec un peu d’humour :
Le module Panic ressemblera à ça :
Bon tout de suite, vous comprenez mieux mon avertissement envers l’humour de la fin de cet article
Ce module aura pour but de démontrer l’utilisation de notre méthode ShowError qu’expose notre service Prompt précédemment créé. En effet, le module nous propose deux choix impossible:D
Le premier choix nous génère une exception OutOfMemoryException… Impossible de charger Linux, il est bien trop bouffi… Et le deuxième, une exception de type KeyNotFoundException… Mac OS X n’a pas assez de part de marché pour notre application, enfin normal n’est-ce pas une simple erreur d’arrondie ?
(Quoi ? Je troll ?)
Passons au code, nous voulons respecter un pattern MVVM pour cette vue dont voici le code :
Grâce à l’EventAggregator, notre ViewModel est découplé totalement du service Prompt, il publie simplement une demande d’affichage d’exception en fonction d’un bouton cliqué par l’utilisation. Pratique n’est-ce pas ? En plus, on respecte totalement le pattern MVVM…
Le module Question est très proche de celui-ci, par contre celui est un peu plus couplé au service de prompt, en effet pour la démo j’ai préféré vous montrer les deux possibilités, injection du service ou EventAggregator. Il est envisageable que le service de prompt intercepte des événements pour afficher une MessageBox, je vous laisse le faire. Pratiquer, c’est apprendre.
Ce module pose simplement une question « Allez-vous installer Windows 7 ? » une question simple dont la réponse est évidente, mais on la pose quand même pour la démo
Enfin la partie du ViewModel utilisant le service Prompt :
1privatevoidAskQuestion(objectobj)
2 {
3this._promptService.ShowMessageBox("Permettez moi une question.", "Allez-vous installer Windows 7 ?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, this.AskQuestionCompleted);
Pour conclure, nous avons vu qu’il est nécessaire de dédier un service à l’affichage des prompts pour respecter le pattern MVVM. Il ya plusieurs possibilités, notamment celle de Thomas Lebrun qui a comme avantage d’utiliser aucune libraire ou celle que je vous ai présentée qui exploite les fonctionnalités de Prism. J’espère que cet article vous a plus.
0 réponses
Suivez la conversation, abonnez-vous au flux RSS des commentaires..