Mon application Silverlight/WPF et la guerre des patterns 08/02/2011 Cyril CATHALA Architecte So@t Olivier NAVARRE Architecte So@t Cédric DAVIAUD Directeur Technique So@t
Sommaire Introduction Rappels Application Composite SoPrism ! Inversion of Control MVVM Application Composite Présentation PRISM SoPrism ! Présentation Conseils pour bien démarrer Découpage Communication Outils Validation de données
Introduction
So@t en quelques mots … SSII spécialisé dans le développement 10 ans de savoir-faire .Net / Java 220 collaborateurs Tous les métiers du développement 3 ans d’expertise Silverlight / WPF Société Générale, Dexia, Vente Privée, Canal+, M6, Radio France, Crédit Agricole, Eurosport, Sarenza.com
Introduction Nos « Bonnes pratiques » Découplage ! Application de référence « HappyNet » Application composite Nos « Bonnes pratiques » Découplage ! Quickstart : Template SoPrism Génération d’un squelette applicatif Mise à disposition de briques techniques date
of control Inversion Rappels !
Inversion of control Problématique : Résolution : Dépendance très forte entre les classes composants / services Résolution : Déporter l’instanciation des dépendances des classes à un composant externe (Container)
Framework Unity Container IoC : UnityContainer Injection de dépendances Constructeur Propriété v2.0 - mai 2010 Microsoft Patterns & Practices Enterprise Library
Framework MEF Utilise un container IoC : CompositionContainer Injection de dépendances Constructeur Propriété Inclus dans .NET 4 .0 / Silverlight 4.0
Unity vs MEF MEF - Unity Ce qu’ils font tous les 2 : Enregistrement de types Enregistrement d’instances Création d’instance des types enregistrés uniquement Injection d’instances via constructeur / propriétés Attributs pour marquer types et dépendances Résolution des dépendances dans un graphe d’objets
Unity vs MEF Unity MEF Ce qui les distingue : Centralisé Décentralisé Résolution de classes concrètes sans enregistrement Résolution de génériques Interception Nombreux retours d’expérience Découverte d’assemblies à la volée Téléchargement de XAP Recomposition à chaque nouvelle découverte Exporte automatiquement les types dérivés Livré avec le Framework .NET date
Utilisation d’Unity Exemples de code Configuration du Container : Résolution : Enregistrement d’un type this.container.RegisterType<IMyView,MyView>(); Enregistrement d’un singleton this.container.RegisterType<IMyView,MyView>( new ContainerControlledLifetimeManager()); Récupération d’une instance IView view = this.container.Resolve<IView>(); date
Utilisation d’Unity Par injection Exemples de code (suite) public class MyViewModel { public IMyView View { get; private set; } // Constructeur public MyViewModel(IMyView view) this.View = view; } // Exemple d’instanciation d’un MyViewModel MyViewModel viewModel = this.container.Resolve<MyViewModel>();
Model-View-ViewModel Rappels !
Sans Binding Code behind Only : View XAML Modèle de données Event Handlers Modèle de données date
Pattern MVVM Problématique : Résolution : Collaboration développeur / designer difficile Tests fastidieux (plus que d’habitude !) Code-behind difficilement maintenables Résolution : Utiliser le pattern Model-View-ViewModel ! date
Pattern MVVM View.DataContext = ViewModel PropertyChanged DataBinding XAML (Code-Behind) PropertyChanged DataBinding Commands ViewModel Etat Opérations Modèle de données Web Service date
Interface utilisateur Persistence de données Pattern MVVM View (xaml) ViewModel (cs) Model (cs) Interface utilisateur Logique applicative Persistence de données Design pur XAML seulement Code-behind minimal Basé sur un UserControl Data Binding vers la classe ViewModel (Presque) aucun gestionnaire d’évènements Classe adapteur entre la vue et le modèle « Englobe » le modèle pour le rendre exploitable par la vue Notifications (implémente INotifyPropertyChanged) Expose des Commands Se charge de la validation Testable Données pures Pas de concepts visuels Couche d’accès aux données
Pattern MVVM : View First Définition La View assigne le DataContext au ViewModel Disponible au design time (support de Blend) Pas d’IoC possible en XAML View <UserControl.DataContext> <my:PageViewModel /> </UserControl.DataContext> En XAML
Pattern MVVM : View First D’autres méthodes d’affectation par le code… public MyView() { InitializeComponent(); DataContext = new MyViewModel(); } Dans le constructeur public MyView(IMyViewModel viewModel) { InitializeComponent(); DataContext = viewModel; } A l’extérieur var viewModel = new MyViewModel(); var view = new MyView(viewModel);
Pattern MVVM : ViewModel First Définition Le ViewModel affecte le DataContext de la View Méthode préconisée ViewModel pilote la View IoC possible et donc testable View Model public MyViewModel(IMyView myView) { myView.DataContext = this; } Par le code
Application Composite Prism
Prism - Présentation Composite Application Guidance Microsoft P&P Team Guidance & Framework Open source (Codeplex) Composants pour construire des applications composites Se base sur un conteneur IoC MVVM “compliant” Version 4.0 Compatible Silverlight 4.0 / WPF 4.0 / WP7
Prism – Pourquoi ? Faible couplage Configurabilité Composabilité Maintenance / lisibilité Testabilité accrue Multi-platform (WPF / Silverlight / WP7)
Prism – Vocabulaire Bootstrapper Shell Module Initialisation de la configuration Shell RootVisual / MainWindow Conteneur visuel principal Module Découpage vertical des fonctionnalités
Prism - Bootstrapper Initialise les services Charge les modules Application Bootstrapper Conteneur Services Shell Modules Initialise les services Charge les modules Lance le Bootstrapper Injection de dépendances Gestion de régions d’affichage Gestion de messages Découpage vertical des fonctionnalités RootVisual MainWindow
Prism – Services Prism contient différentes briques techniques : Un gestionnaire de Module : > BootStrapper Un gestionnaire de régions d’affichage : > RegionManager Une implémentation de ICommand : > CommandDelegate Un gestionnaire d’évènements : > EventAggregator …
Template d’application SoPrism
SoPrism Template applicatif se basant sur : Et permettant : Framework Prism Pattern MVVM Et permettant : La génération d’un squelette applicatif De concentrer des « bonnes pratiques »
SoPrism Templates Visual Studio 2010 Snippets Composants Template de solution d’application Silverlight Template de Projet Module (Prism) Template de « bloc visuel » MVVM Snippets Composants Contrôles Interactivité (Triggers/Actions/Behaviors) Validation Gestion des logs Gestion des exceptions Converters
SoPrism Services Prism Business Data Shell Infrastructure Module Vue date
Services Prism Business Business Business Data Data Data Shell Configuration Helpers Logging Shell Infrastructure Prism Interactivity Validation Controls Converters Module Module Module Vue Vue Vue Services Logging Business Business Business Data Data Data date
SoPrism : génération d’un squelette applicatif [ Démo ] SoPrism : génération d’un squelette applicatif date
Prism - RegionManager Problématique : Résolution : Une vue peut contenir d’autres vues : couplage fort Résolution : Confier l’injection d’une vue à un gestionnaire de régions d’affichage Shell Region Vue date
SoPrism : Utilisation du RegionManager [ Démo ] SoPrism : Utilisation du RegionManager date
Comment bien découper son application ? date
Hello Board ! Description Application composite de démonstration basée sur le Template SoPrism Respecte nos « bonnes pratiques » ScrumBoard en Silverlight date
Hello Board ! Vocabulaire Scrum UserStory : fonctionnalité BackLog : liste de fonctionnalités Tâche : partie de fonctionnalité Sprint : itération ScrumBoard : tableau des tâches d’un sprint Un backlog est une liste de fonctionnalité, c-à-d constituée de UserStories chacunes découpées en tâches, ou sous-fonctionnalités, qui sont traitées au courts d’une itération aka sprint date
[ Démo ] HelloBoard date
Définition des modules Besoin : Découper l’application par modules applicatifs et par blocs visuels 2 approches possibles En mettant en avant le visuel En mettant en avant le métier
Définition des modules Découpage orienté visuel Module Sprint Module User Vue Sprint Vue UserList Vue Task Vue UserDetail Vue UserSelection Vue Page Scrumboard Page User Manager date
Définition des modules Découpage orienté métier Module Sprint Module User Vue Sprint Vue UserList Vue Task Vue UserDetail Injection Vue UserSelection Vue Page Scrumboard Page User Manager date
Hello Board ! Conception Découpage orienté métier date
Comment communiquer entre les couches ? date
Gestionnaire d ’Evènements EventAggregator View Model View Model FAIL! View Model View Model View Model View Model View Model View Model
Gestionnaire d ’Evènements EventAggregator View Model View Model View Model View Model Event Aggregator View Model View Model View Model View Model
Gestionnaire d’évènements EventAggregator Problématique : Comment dialoguer entre 2 modules (classes) ne se connaissant pas ? Résolution : Confier la gestion d’évènements à un gestionnaire Pattern Publish-Subscribe Couplage faible de la communication entre classes date
Gestionnaire d ’Evènements EventAggregator Comment transitent les données d’un module à un autre ? Module Sprint Module User Vue UserSelection UserService Reference Vue TaskView IdUser Event Aggregator SprintService Reference SprintService.User UserService.User SprintService Data UserService date
Gestionnaire d ’Evènements EventAggregator Exemples de code Création d’un CompositePresentationEvent<EventArgs> public class UserSelectedEvent : CompositePresentationEvent<UserSelectedEventArgs> { } public class UserSelectedEventArgs public int IdUser { get; private set; } public UserSelectedEventArgs(int idUser) this.IdUser = idUser; date
Gestionnaire d ’Evènements EventAggregator Exemples de code Publication d’un Event this.eventAggregator.GetEvent<UserSelectedEvent>() .Publish(new UserSelectedEventArgs(idUser)); Abonnement à un Event this.eventAggregator.GetEvent<UserSelectedEvent>() .Subscribe(this.OnSelectedUser); date
Communication inter-module [ Démo ] Communication inter-module date
Validation
INotifyDataErrorInfo Validation 3 approches possibles : Validation Exceptions IDataErrorInfo INotifyDataErrorInfo Setter { throw; } Synchrone Limité à 1 erreur par propriété Confusion entre erreur de saisie et exception applicative Interface Compatibilité WPF Asynchrone Plusieurs erreurs par propriété, non limitées aux strings HasErrors : indique si actuellement valide ou non Silverlight 4 « compliant »
Validation Interface INotifyDataErrorInfo public interface INotifyDataErrorInfo { bool HasErrors { get; } event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; IEnumerable GetErrors(string propertyName); } date
Validation Manager (SoPrism) Exemples de code Initialisation this.validationManager = new ValidationManager(RaiseErrorsChanged); Définition d’une règle this.validationManager.SetRuleFor(() => this.FirstName, new RequiredStringValidator(), "Merci de renseigner le prénom"); Validation d’une règle this.validationManager.ValidateRules(() => this.FirstName); this.validationManager.ValidateAllRules(); date
[ Démo ] Validation Manager
[ http://soprism.codeplex.com ] Code source Disponible sur Codeplex dès … … maintenant ! [ http://soprism.codeplex.com ] date
Quelques références [ http://happynet.codeplex.com ] [ http://happynet.codeplex.com ] [ http://silverlight.soat.fr ] [ http://blog.soat.fr ]
[ Nathalie Pettier ] [ Nathanaël Marchand ] Merci à : [ Nathalie Pettier ] [ Nathanaël Marchand ]
Des questions ? ? Des réponses !
Merci !