Temps de lecture : 5 min.

Accéder à la partie 1

Après une première partie qui s’est concentrée sur l’organisation d’un projet WPF/Silverlight, attaquons-nous à quelque chose de plus compliqué avec les techniques avancées…

Silverlight et WPF en entreprise : retours d’expérience, bonnes pratiques et techniques avancées par Luc Vo Van (Microsoft Services) et Thomas Lebrun (Access-IT)

Architecture type

WPF et Silverlight nous offrent la possibilité de développer des applications composites. Le pattern Models-Views-ViewModels dit MVVM est favorisé.

Principes fondamentaux

Composition

Développer une application par composition c’est créer une coquille vide à laquelle vient se greffer différents modules applicatifs  indépendants qui interagissent ensemble pour les besoins de notre application. Le principal intérêt de ce type d’application est qu’il est possible de paralléliser les développements. En effet, puisque chaque module est indépendant, il est simple de regrouper les équipes en unités fonctionnelles. Ces unités fournissent alors aux autres équipes l’interface de leur implémentation ce qui permet d’exécuter toute l’application même si le développement d’un module n’est pas totalement finalisé.

Avec ce mécanisme, il est alors très aisé d’activer ou de désactiver une fonctionnalité. La maintenabilité de l’application est également largement revue à la hausse grâce au découpage par modules. Chaque module étant cloisonné et hiérarchisé, l’évolution d’un module ne sera visible de l’extérieur qu’à travers son interface. L’impact sur les autres modules est donc fortement limité.

Généralement, on observe deux approches d’architecture de développement par composition

Inversion of control

Plus communément appelé Ioc, l’Inversion of Control est un pattern utilisé pour l’injection de dépendance. On l’appelle  également parfois injection de code car en favorisant un découplage fort entre l’interface (UI) et l’implémentation (UI logic), il facilite la substitution des composants par des mocks lorsqu’un module n’est pas encore disponible.

Diverses implémentations sont possibles via Unity, Spring .NET, Ninject

Commanding

Le système de commande est un mécanisme qui permet l’exécution d’actions déclenchées par des contrôles de l’IHM. Par exemple, sur un contrôle de type Button, habituellement, les actions qui suivent le clic sur celui-ci sont codés dans l’événement Click du code behind. Avec le système de commande, on écrit les actions dans la commande et il suffit alors de préciser au bouton le nom de cette commande à exécuter.

Ce mécanisme permet donc de découpler la source de l’action car les actions ne dépendent plus de l’événement Click. Il y a donc peu d’adhérence à l’interface graphique. Autre point fort, une commande peut également être affectée à plusieurs contrôles à la fois.

L’objectif du système de commande est de découpler l’interface graphique du code appelé lors de l’exécution d’une action. Un événement CanExecute intervient dans la définition d’une règle d’activation de la commande. Cette règle, lorsqu’elle est satisfaite active alors les contrôles de l’IHM affectés par cette commande.

Exemple :

//Création de la commande MyCommand
public static RoutedUICommand MyCommand { get; set; }

//Execute : Actions de la commande
private void ShowMessage_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show(“Hello world”);
}

//CanExecute : Condition d’activation de la commande
private void ShowMessage_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if(condition)
e.CanExecute = true;
}

//Dans le construteur, on enregistre les événements Executed et CanExecute
CommandBinding myBinding = new CommandBinding(MyCommand);
myBinding.CanExecute += ShowMessage_CanExecute;
myBinding.Executed += ShowMessage_Executed;
CommandBindings.Add(myBinding);

MVVM :

<!-- Après avoir inscrit la commande MyCommand au ViewModel et enregistré dans le ViewModel dans le DataContext de la View, on affecte la commande par Binding-->
<Button x:Name="MyButton" Command="{Binding MyCommand}" />

Non MVVM :

//Dans le constructeur, on affecte au bouton MyButton la commande MyCommand
myButton.Command = MyCommand;

Messaging

Par messaging, on entend l’implémentation d’un mécanisme permettant le dialogue entre les modules. Ce mécanisme, appelé Messenger, doit permettre aux modules de publier un événement et de notifier le ou les modules qui y sont abonnés. On parle alors de communication inter-ViewModels. Il est également possible de notifier un service du Shell par cette méthode pour par exemple faire appel au log, etc…

On distingue généralement les différents types d’implémentations suivantes :

Services du Shell

Les services du Shell regroupent tous les composants qui permettent aux Views, Viewmodels et Controls de communiquer avec l’hôte. Ce sont les services transverses  tels que les Popups Messages, Ribbon Menu, Logging, Workspaces, etc …, et qui peuvent être utilisés à tout instant.

Approches par patterns:

Avec le pattern MVVM, nous avons vu qu’il existe une séparation forte entre le View et le ViewModel. Cette séparation permet 2 approches :

  • ViewModelFirst : Dans cette configuration, le ViewModel s’occupe de créer les différents Views et de définir les bindings associés.
  • ViewFirst : Dans cette configuration, la View utilise le ViewModel comme ressource. Le binding est alors défini dans la vue. Ce pattern est celui qui est utilisé par le Framework Prism.

Pour des applications implémentant des formulaires, l’utilisation du ViewState permet de définir l’état d’un formulaire avec des changements d’états plus simples et plus légers. En effet, il suffit de ne créer qu’une seule View qui contient alors les différents états du formulaire. Introduit par Silverlight et désormais présent dans WPF 4.0, le ViewState ajoute également des animations de transitions issues du Blend SDK à travers l’utilisation de behaviours.

Parfois, il arrive qu’on ait besoin de créer une fonctionnalité supplémentaire pour un contrôle existant. Dans ce cas de figure, nous utilisons alors les Attached Property qui permettent d’étendre un contrôle existant avec notre fonctionnalité, sans avoir à procéder à un héritage.

Blend SDK:

Ce SDK fournit entre autre des behaviours qui permettent de greffer des  fonctionnalités sur des contrôles pour avoir des comportements supplémentaires.

  • InvokeCommandAction : Permet de lier un événement à une commande sans envoyer de paramètre.
  • DataStateBehaviour : Sélectionne un VisualState en fonction d’une propriété
  • CallMethodAction : Permet à un événement d’exécuter une méthode.

Pour utiliser ces behaviours, il faut au préalable charger l’assembly System.Window.Interactivity (2 dlls). Il est également possible d’écrire ses propres extensions.

Testing

Le pattern MVVM d’une manière générale est le pattern de prédilection pour mettre en place des tests unitaires automatisés pour les applications WPF et Silverlight. Nous avons vu auparavant que la mise en place d’un pattern d’IoC facilitait l’écriture de Mocks. Et bien, c’est avec cette même facilité qu’on pourra également mettre en place une série de tests automatisés car l’UI et l’UI logic sont complètement décorrélés dans cette architecture.

Pour réaliser vos tests selon le type d’application :

WPF

Silverlight

Les principaux outils de testing sont déjà intégrés à Visual StudioLe ViewModel est testable avec UnitTesting

La View est testable avec UI Automation

Contrairement à WPF, Silverlight ne dispose pas d’outil de test intégré à Visual Studio – peut-être dans une future version ? -, cependant, les tests unitaires sont réalisables à l’aide de Silverlight Unit Test Framework.Le ViewModel est testable avec Statlight, Odin, …

Liens externes

MVVM : Framework Prism, Microsoft Enterprise Library

Inversion of Control : UnityFramework Spring .NETNinject

Behaviour : Blend SDK

Testing : Silverlight Toolkit (Silverlight Unit Test Framework)