41
L’architecture MVVM MVVM Light Toolkit Jean-Baptiste Vigneron @jbvigneron David Bottiau @dbottiau

Pattern MVVM avec MVVM Light Toolkit

Embed Size (px)

DESCRIPTION

Découvrez comment implémenter le pattern MVVM à l'aide du MVVM Light Toolkit

Citation preview

Page 1: Pattern MVVM avec MVVM Light Toolkit

L’architecture MVVMMVVM Light Toolkit

Jean-Baptiste Vigneron@jbvigneron

David Bottiau@dbottiau

Page 2: Pattern MVVM avec MVVM Light Toolkit

Sommaire• Introduction

– Présentation– MVVM Light Toolkit– Comparaison rapide entre MVVM, MVC et MVP

• Notes pour cette présentation• Architecture• Les ViewModels

– Données– Commandes– Modes de binding

• Le ViewModelLocator• Les Design ViewModels• La Messagerie

Page 3: Pattern MVVM avec MVVM Light Toolkit

Présentation

• MVVM est un patron de conception (design-pattern) conçu à la base pour les applications .NET

• Model (Modèle) – View (Vue) – ViewModel (Vue-Modèle)

• Permet une separation entre le traitement des données et la façon dont elles sont affichées

• Utilisation du principe de “binding” au maximum

Page 4: Pattern MVVM avec MVVM Light Toolkit

MVVM Light Toolkit• Framework libre et gratuit facilitant l’implémentation du

pattern MVVM

• Crée par Laurent Bugnion (GalaSoft)

• Utilisable avec:• WPF• Windows 10• Xamarin• Silverlight

• Site officiel: http://www.galasoft.ch/mvvm/

• Téléchargeable via NuGet

Page 5: Pattern MVVM avec MVVM Light Toolkit

Comparaison rapide entreMVVM, MVC et MVP

Page 6: Pattern MVVM avec MVVM Light Toolkit
Page 7: Pattern MVVM avec MVVM Light Toolkit

Architecture

• DataService• Classes d’accès à une ressource externe (base de données,

webservice, flux JSON, flux RSS, etc…)

• Model• Des classes simples (POCO) représentant les données

• ViewModel• Des fichiers .cs

• Vue• Des fichier .xaml et .xaml.cs

Page 8: Pattern MVVM avec MVVM Light Toolkit

Architecture• Une Vue = un ViewModel• Un ViewModel peut être lié à plusieurs vues (mais pas l’inverse)

• Le ViewModel est l’adaptation du modèle pour la vue. Son rôle est donc autre que celui du Contrôleur MVC.

• Le ViewModel et la Vue sont liés en utilisant le binding

• Liaison des contrôles d’affichage (ListBox/ComboBox/TextBox…) à des données

• Liaison des contrôles d’action (Button/Image/Slider…) à des commandes

Page 9: Pattern MVVM avec MVVM Light Toolkit

Notes pour cette présentation

• Les exemples utilisés s’appuient sur un projet de gestion de cave à vins réalisé avec WPF.

• Pour simplifier l’implémentation de MVVM, nous avons utilisé le framework MVVM Light Toolkit.

Page 10: Pattern MVVM avec MVVM Light Toolkit

Exemple de Model

public class Vin{ public string Nom { get; set; } public string Appellation { get; set; } public Region Region { get; set; } public int Annee { get; set; }}

La classe la plus simple possible (POCO)

Page 11: Pattern MVVM avec MVVM Light Toolkit

Exemple de ViewModel

public interface IMainViewModel{ public string Titre { get; set; } public ObservableCollection<Vin> Vins { get; }

public ICommand ChargerVinsCommand { get; }}

Déclaration dans une interface

Données

Commandes

Page 12: Pattern MVVM avec MVVM Light Toolkit

Exemple de ViewModelpublic class MainViewModel : ViewModelBase, IMainViewModel{ public MainViewModel() { Vins = new ObservableCollection<Vin>(); ChargerVinsCommand = new RelayCommand(ChargerVins); }

private string _titre;

public string Titre { get { return _titre; } set { _titre = value; RaisePropertyChanged(); } }

public ObservableCollection<Vin> Vins { get; private set; }

public ICommand ChargerVinsCommand { get; private set; }

private void ChargerVins() { WebserviceClient client = new WebServiceClient(); IList<Vin> vins = client.GetVins();

        Vins.Clear();

        foreach (var vin in vins)            Vins.Add(vin); }}

Implémentation

Données

Commandes

Initialisation

Page 13: Pattern MVVM avec MVVM Light Toolkit

Composition du ViewModel• Le ViewModel contient les données affichées dans

la Vue• Il permet de faire abstraction de la manière dont elles sont

présentées• On va agir directement sur les données et non plus sur les

contrôles graphiques

• Le ViewModel contient les commandes (actions métiers) de la Vue• Afficher ou modifier une donnée est une action métier• Lancer une animation ou masquer un contrôle n’en est pas une

• Chaque classe ViewModel hérite de la classe ViewModelBase, provenant de MVVM Light Toolkit

Page 14: Pattern MVVM avec MVVM Light Toolkit

Focus : Binder une donnée

public class MainViewModel : ViewModelBase, IMainViewModel{ private string _titre;

public string Titre { get { return _titre; } set { _titre = value; RaisePropertyChanged(); } }}

Page 15: Pattern MVVM avec MVVM Light Toolkit

Focus : Binder une donnée• Côté ViewModel:

• Pour une donnée, on crée un attribut privé et une propriété publique

• Dans le setter, on appelle la méthode RaisePropertyChanged. Cette méthode permet d’avertir la Vue que la donnée a été modifiée

• RaisePropertyChanged est contenu dans la classe ObservableObject dont ViewModelBase hérite. Cette classe implémente l’interface INotifyPropertyChanged.

• Côté Vue:• Veiller à ce que le mode de binding soit approprié pour le

rafraîchissement des données.

Page 16: Pattern MVVM avec MVVM Light Toolkit

Focus : Binder une collection de données

public class MainViewModel : ViewModelBase, IMainViewModel{ public MainViewModel() { Vins = new ObservableCollection<Vin>(); }

public ObservableCollection<Vin> Vins { get; private set; }}

Page 17: Pattern MVVM avec MVVM Light Toolkit

Focus : Binder une collection de données• Côté ViewModel:

• On utilise le type ObservableCollection<T> (et non List<T> ou des T[])

• Cette classe implémente déjà INotifyPropertyChanged, ainsi que d’autres interfaces pour le rafraîchissement automatique des données.

• Côté Vue:• Veiller à ce que le mode de binding soit approprié pour le

rafraîchissement des données.

Page 18: Pattern MVVM avec MVVM Light Toolkit

Modes de binding

• OneWay

• TwoWay

• OneTime

• OneWayToSource

• Default

La destination est mise à jour lorsque la source est mise à jour, mais pas l’inverse.

La destination est mise à jour lorsque la source est modifiée et vice-versa.

La destination prend en compte un seul changement de la source. Si la source est de nouveau mise à jour, la destination n’est pas actualisée.

La source est mise à jour lorsque la destination est mise à jour mais pas l’inverse. Pas toujours disponible en fonction de la technologie (ex: Windows Phone)

Utilise le comportement par défaut pour le contrôle concerné. (ex: OneWay pour Label et TwoWay pour TextBox)

Source: donnée du ViewModelDestination: valeur du contrôle graphique de la vue

Page 19: Pattern MVVM avec MVVM Light Toolkit

public class MainViewModel : ViewModelBase, IMainViewModel{ public MainViewModel() { ChargerVinsCommand = new RelayCommand(ChargerVins); }

public ICommand ChargerVinsCommand { get; private set; }

private void ChargerVins() { Vins.Clear(); WebServiceClient client = new WebServiceClient();    foreach (Vin vin in client.GetVins()) { Vins.Add(vin); }   }}

Focus : Binder une commande

Page 20: Pattern MVVM avec MVVM Light Toolkit

• Côté ViewModel:• Les actions métiers sont représentées par des méthodes (idéalement

privées, mais il se peut de devoir les rendre publiques pour nos tests unitaires)

• On lie ces méthodes à des commandes (objets de type ICommand)

• Le constructeur de RelayCommand prend 1 ou 2 paramètres1. Obligatoire : La méthode qui sera appelée lors du déclenchement de la

commande2. Facultatif : La méthode qui détermine si la commande peut être exécutée ou

non

• La méthode associée à la commande peut prendre un (et un seul) paramètre. On utilise pour celà la classe RelayCommand<T>.

• Côté Vue:• Les contrôles graphiques sont bindés aux commandes à l’aide de l’attribut

Command (et CommandParameter si un paramètre est passé)

Focus : Binder une commande

Page 21: Pattern MVVM avec MVVM Light Toolkit

Exemple d’une View

<Page x:Class="CaveAVins.View.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      Height="1024" Width="1280"      Title="Accueil"      DataContext="{Binding MainVM, Source={StaticResource Locator}}">

    <Grid> <TextBox Text="{Binding Titre, Mode=TwoWay}" />

         <ListBox ItemsSource="{Binding Vins, Mode=OneWay}" />

<Button Command="{Binding ChargerVinsCommand}" />    </Grid></Page>

Page 22: Pattern MVVM avec MVVM Light Toolkit

Info sur les commandes• Tous les contrôles graphiques ne proposent pas d’attribut

Command.

• Tous les évènements ne peuvent pas être traduits directement en commande (ex: SelectionChanged, IsEnabled, etc…).

Heureusement, il existe deux alternatives.

• 1. Utiliser un EventTrigger dans le XAML :

<i:Interaction.Triggers>    <i:EventTrigger EventName="SelectionChanged">        <command:EventToCommand Command="{Binding ChargerVinsCommand}" />    </i:EventTrigger></i:Interaction.Triggers>

Page 23: Pattern MVVM avec MVVM Light Toolkit

Info sur les commandes

private void lstVins_SelectionChanged(object sender, EventArgs e){ IMainViewModel vm = (IMainViewModel)DataContext; vm.ChargerVinsCommand.Execute(null);}

• Tous les contrôles graphiques ne proposent pas d’attribut Command.

• Tous les évènements ne peuvent pas être traduits directement en commande (ex: SelectionChanged, IsEnabled, etc…).

Heureusement, il existe deux alternatives.

• 2. Créer un évènement et appeler la commande :

Page 24: Pattern MVVM avec MVVM Light Toolkit

En bref sur les ViewModels…• Le ViewModel est une adaptation du Modèle pour la Vue

• Un ViewModel comporte des données et des commandes• Un ViewModel hérite de ViewModelBase

• ViewModelBase implémente INotifyPropertyChanged pour notifier la Vue qu’une donnée a été modifiée• Pour une donnée simple, on appelle RaisePropertyChanged• Pour les collections, on utilise les ObservableCollection<T>

• Une commande peut prendre un (et un seul) paramètre.

• Si on veut faire des tests unitaires, ceux-ci peuvent être faits sur la couche ViewModel.

Page 25: Pattern MVVM avec MVVM Light Toolkit

ViewModelLocator

• Cette classe recense les ViewModels de notre application

• Elle permet d’ailleurs un accès facile à nos ViewModels

• Elle permet de lier la couche Vue et ViewModel• , et permet d’y accéder facilement.

• Elle recense également tous les Design ViewModels qui seront utilisés par Visual Studio et Blend.

• Elle comporte également des méthodes pour décharger les ViewModels de la mémoire vive lorsque ceux-ci ne sont plus utilisés.

Page 26: Pattern MVVM avec MVVM Light Toolkit

ViewModelLocatorpublic class ViewModelLocator{ static ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); }

public static IMainViewModel MainVM { get { return ServiceLocator.Current.GetInstance<IMainViewModel>(); } }

public static void CleanMain() { SimpleIoc.Default.Unregister<IMainViewModel>(); SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); }

public static void Cleanup() { CleanMain(); }}

Page 27: Pattern MVVM avec MVVM Light Toolkit

ViewModelLocator• On initialise le ServiceLocator dans le constructeur statique• On référence ensuite les ViewModels de notre application à l’aide

de la classe SimpleIoC (fournie par MVVM Light Toolkit).

public class ViewModelLocator{ static ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); }}

Page 28: Pattern MVVM avec MVVM Light Toolkit

public class ViewModelLocator{ public static IMainViewModel MainVM { get { return ServiceLocator.Current.GetInstance<IMainViewModel>(); } }}

ViewModelLocatorOn déclare ensuite une propriété statique pour chaque ViewModel afin d’y avoir un accès rapide depuis n’importe quelle autre classe, et notamment la couche Vue.Le ServiceLocator utilise des singletons. Si l’instance existe, elle est retournée sinon elle est créée puis retournée.

Une exception est levée si le ViewModel n’a pas été enregistré au préalable

Page 29: Pattern MVVM avec MVVM Light Toolkit

public class ViewModelLocator{ public static void CleanMain() { SimpleIoc.Default.Unregister<IMainViewModel>(); SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); }}

ViewModelLocatorLorsqu’un ViewModel n’est plus nécessaire, il faut le retirer de la mémoire.

On crée une méthode de nettoyage, qui va désallouer le ViewModel de la mémoire, puis le re-préparer en cas de besoin. Il ne sera réinstancié que si la propriété MainVM est appelée de nouveau.

Page 30: Pattern MVVM avec MVVM Light Toolkit

public class ViewModelLocator{ public static void Cleanup() { CleanMain(); }}

ViewModelLocatorEnfin, on peut créer une méthode Cleanup qui appelle toutes les autres méthodes Clean.

Elle peut être appelé lorsque on a besoin de réinitialiser l’application par exemple.

Page 31: Pattern MVVM avec MVVM Light Toolkit

Liaison couches Vue et ViewModel• Pour lier les 2 couches, on utilise le ViewModelLocator. On le référence

dans le fichier App.xaml

• On déclare une ressource de type ViewModelLocator (nommée ici Locator)

• Exemple d’App.xaml dans un projet WPF:<Application x:Class="CavesAVins.View.App"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"             xmlns:viewModel="clr-namespace:CaveAVins.ViewModel;assembly=CaveAVins.ViewModel"             StartupUri="MainWindow.xaml">    <Application.Resources>        <ResourceDictionary>            <viewModel:ViewModelLocator x:Key="Locator" />        </ResourceDictionary>    </Application.Resources></Application>

Page 32: Pattern MVVM avec MVVM Light Toolkit

Liaison couches Vue et ViewModel• Une fois la declaration du Locator faite, on peut aller chercher

le ViewModel de notre page.

• On lie le ViewModel à la propriété DataContext de la Vue

<Page x:Class="CaveAVins.View.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      Height="1024" Width="1280"      Title="Accueil"      DataContext="{Binding MainVM, Source={StaticResource Locator}}">

Page 33: Pattern MVVM avec MVVM Light Toolkit

Les Design ViewModels

• Ces classes contiennent des jeux de données fictives.

• Ces données fictives sont affichées directement dans Visual Studio et Blend.

• Une classe Design ViewModel hérite d’un ViewModel. Pour chaque ViewModel, on peut donc créer un Design ViewModel.

• Ainsi, il est possible de créer le rendu (les templates) de son application avec des données fictives sans devoir lancer/debugger l’application.

Page 34: Pattern MVVM avec MVVM Light Toolkit

Les Design ViewModelsOn peut référencer nos Design ViewModels dans le ViewModelLocator à l’aide de la propriété IsInDesignModeStatic de ViewModelBase.Cette propriété est disponible partout et tout le temps.public class ViewModelLocator{ static ViewModelLocator() { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

if (ViewModelBase.IsInDesignModeStatic) { // Mode Design pour Visual Studio en Blend SimpleIoc.Default.Register<IMainViewModel, MainDesignViewModel>(); } else { // Mode Debug / Run SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(); } }}

Page 35: Pattern MVVM avec MVVM Light Toolkit

Les Design ViewModels

• Exemple

public class MainDesignViewModel : MainViewModel{ public MainDesignViewModel() { Vins = new ObservableCollection { new Vin { Nom = “La Tour Carnet”, Appellation = “Haut-Médoc”, Region = Region.Bordeaux }, new Vin { Nom = “Merlot”, Annee = 2011, Region = Region.Rhone }, new Vin { Nom = “Cuvée-Silex”, Appellation = “Pouilly-Fumé”, Region = Region.Loire },

} }}

Page 36: Pattern MVVM avec MVVM Light Toolkit

Messagerie

• La messagerie est fournie par MVVM Light Toolkit

• Elle permet d’envoyer des messages entre les classes

• Un message est un objet de n’importe quel type

• Elle permet, par exemple, d’envoyer des messages entre:• Un ViewModel et une Vue• Deux ViewModels• Deux classes quelconques

• Ce mécanisme est une solution de contournement lorsqu’aucune autre ne permet de transmettre un objet d’une classe à un autre

Page 37: Pattern MVVM avec MVVM Light Toolkit

Messagerie

public class ExempleViewModel : ViewModelBase, IExempleViewModel

public ExempleViewModel() { EnvoyerPagesCommand = new RelayCommand<string>(EnvoyerMessage); }

public Icommand ChangerPagesCommand { get; private set; }

public void EnvoyerMessage(string message) { MessengerInstance.Send(message); }}

• Côté ViewModel: Envoi du message

Page 38: Pattern MVVM avec MVVM Light Toolkit

Messagerie

public class MainWindow

public MainWindow() { InitializeComponent(); Messenger.Default.Register<string>(this, ChangerPage); }

private void AfficherMessage(string message) { Messenger.Show(message); }}

• Côté Vue: Abonnement à réception du message

Page 39: Pattern MVVM avec MVVM Light Toolkit

Messagerie• Pour qu’une classe reçoive un message, on utilise la méthode

Register

• On utilise la méthode Unregister pour la désabonner de la messagerie

• N’importe quelle classe peut envoyer des messages, tout comme n’importe quelle classe peut en recevoir

• Vous ne pouvez envoyer qu’un seul message à la fois, mais un message peut être un objet de n’importe quel type

• Contrôlez-bien si votre message doit être reçu par une ou plusieurs classes simultanément en utilisant :• Correctement les méthodes Register et Unregister.• Un token pour créer des canaux réservés et différencier les

récepteurs

Page 40: Pattern MVVM avec MVVM Light Toolkit

En résumé…• MVVM = Model – View – ViewModel

• MVVM a été conçu pour faciliter la séparation entre la logique et l’affichage

• MVVM utilise la puissance du mécanisme de binding (OneWay/TwoWay)

• MVVM Light Toolkit facilite l’implémentation de MVVM dans une application

• Un ViewModel comporte des données et des commandes

• MVVM facilite les tests unitaires, puisque c’est la couche VM qui peut être testée

• Le ViewModelLocator permet l’accès facile aux ViewModels et gère leur cycle de vie

• Les Design ViewModels contiennent des données fictives qui sont directement affichées dans Visual Studio et Blend

• La messagerie permet de transmettre des objets entre classes

Page 41: Pattern MVVM avec MVVM Light Toolkit