Premiers pas avec mvvm

C# Résumé MVVM et exemple complet

Sommaire:

MVVM est un modèle architectural qui est représenté par trois composants distincts, le Model, View et ViewModel. Afin de comprendre ces trois couches, il est nécessaire de définir brièvement chacune, suivie d’une explication de la façon dont elles fonctionnent ensemble.

Modèle est la couche qui pilote la logique métier. Il récupère et stocke les informations de n’importe quelle source de données pour la consommation par le ViewModel.

ViewModel est la couche qui agit comme un pont entre la View et le Model. Il peut ou non transformer les données brutes du Modèle en une forme présentable pour la Vue. Un exemple de transformation serait : un indicateur booléen du modèle en chaîne de ‘True’ ou ‘False’ pour la vue.

View est la couche qui représente l’interface du logiciel (c’est-à-dire l’interface graphique). Son rôle est d’afficher les informations du ViewModel à l’utilisateur, et de communiquer les modifications des informations en retour au ViewModel.

Ces trois composants fonctionnent ensemble en se référençant les uns les autres de la manière suivante :

  • La View fait référence au ViewModel.
  • Le ViewModel fait référence au Model.

Il est important de noter que View et ViewModel sont capables de communications bidirectionnelles appelées Data Bindings.

L’interface [INotifyPropertyChanged][1] est un ingrédient majeur de la communication bidirectionnelle (liaison de données).

En utilisant ce mécanisme, la View peut modifier les données dans le ViewModel via la saisie de l’utilisateur, et le ViewModel peut mettre à jour la View avec des données qui peuvent avoir été mises à jour via des processus dans le Modèle ou avec les données mises à jour du référentiel.

L’architecture MVVM met fortement l’accent sur la séparation des préoccupations pour chacune de ces couches. Séparer les couches nous profite car :

  • Modularité : L’implémentation interne de chaque couche peut être modifiée ou échangée sans affecter les autres.
  • Testabilité accrue : Chaque couche peut être Unit Tested avec de fausses données, ce qui n’est pas possible si le code du ViewModel est écrit dans le Code-Behind de la View .

La construction :

Créer un nouveau projet d’application WPF [![Créer un nouveau projet d’application WPF][2]][2]

Créez trois nouveaux dossiers dans votre solution : Model, ViewModel et View, et supprimez le MainWindow.xaml d’origine, juste pour prendre un nouveau départ.

[![Créez 3 nouveaux dossiers dans votre solution][3]][3]

Créez trois nouveaux éléments, chacun correspondant à un calque distinct :

  • Faites un clic droit sur le dossier Modèle et ajoutez un élément Classe appelé HelloWorldModel.cs.
  • Faites un clic droit sur le dossier ViewModel et ajoutez une classe élément appelé HelloWorldViewModel.cs.
  • Faites un clic droit sur le dossier Afficher, et ajoutez un élément Window (WPF) appelé HelloWorldView.xaml.

[![Ajouter les trois éléments][4]][4]

Modifiez App.xaml pour pointer vers la nouvelle Vue

<Application x:Class="MyMVVMProject.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MyMVVMProject"
             StartupUri="/View/HelloWorldView.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

Modèle d’affichage :

Commencez par créer le ViewModel en premier. La classe doit implémenter l’interface INotifyPropertyChanged, déclarer un événement PropertyChangedEventHandler et créer une méthode pour déclencher l’événement (source : [MSDN : How to Implement Property Change Notification][5]). Ensuite, déclarez un champ et une propriété correspondante, en veillant à appeler la méthode OnPropertyChanged() dans l’accesseur set de la propriété. Le constructeur de l’exemple ci-dessous est utilisé pour démontrer que le Model fournit les données au ViewModel.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using MyMVVMProject.Model;

namespace MyMVVMProject.ViewModel
{
    // Implements INotifyPropertyChanged interface to support bindings
    public class HelloWorldViewModel : INotifyPropertyChanged
    {
        private string helloString;

        public event PropertyChangedEventHandler PropertyChanged;

        public string HelloString
        {
            get
            {
                return helloString;
            }
            set
            {
                helloString = value;
                OnPropertyChanged();
            }
        }

        /// <summary>
        /// Raises OnPropertychangedEvent when property changes
        /// </summary>
        /// <param name="name">String representing the property name</param>
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public HelloWorldViewModel()
        {
            HelloWorldModel helloWorldModel = new HelloWorldModel();
            helloString = helloWorldModel.ImportantInfo;
        }
    }
}

Modèle:

Créez ensuite le modèle. Comme indiqué précédemment, le Model fournit des données pour le ViewModel en les extrayant d’un référentiel (ainsi qu’en les repoussant vers le référentiel pour les enregistrer). Ceci est démontré ci-dessous avec la méthode GetData(), qui renverra une simple List<string>. La logique métier est également appliquée dans cette couche et peut être vue dans la méthode ConcatenateData(). Cette méthode construit la phrase “Hello, world!” de la List<string> qui a été précédemment renvoyée par notre “dépôt”.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyMVVMProject.Model
{
    public class HelloWorldModel
    {
        private List<string> repositoryData;
        public string ImportantInfo
        {
            get
            {
                return ConcatenateData(repositoryData);
            }
        }

        public HelloWorldModel()
        {
            repositoryData = GetData();
        }

        /// <summary>
        /// Simulates data retrieval from a repository
        /// </summary>
        /// <returns>List of strings</returns>
        private List<string> GetData()
        {
            repositoryData = new List<string>()
            {
                "Hello",
                "world"
            };
            return repositoryData;
        }

        /// <summary>
        /// Concatenate the information from the list into a fully formed sentence.
        /// </summary>
        /// <returns>A string</returns>
        private string ConcatenateData(List<string> dataList)
        {
            string importantInfo = dataList.ElementAt(0) + ", " + dataList.ElementAt(1) + "!";
            return importantInfo;
        }
    }
}

Voir:

Enfin, la Vue peut être construite. Il n’y a rien à ajouter au code derrière pour cet exemple, bien que cela puisse varier en fonction des besoins de l’application. Cependant, quelques lignes ont été ajoutées au XAML. La Window a besoin d’une référence à l’espace de noms ViewModel. Ceci est mappé à l’espace de noms XAML xmlns:vm="clr-namespace:MyMVVMProject.ViewModel". Ensuite, la fenêtre a besoin d’un DataContext. Ceci est défini sur <vm:HelloWorldViewModel/>. Maintenant, l’étiquette (ou le contrôle de votre choix) peut être ajoutée à la fenêtre. Le point clé à ce stade est de s’assurer que vous définissez le Binding sur la propriété du ViewModel que vous souhaitez afficher comme contenu de l’étiquette. Dans ce cas, il s’agit de {Binding HelloString}.

Il est important de se lier à la propriété, et non au champ, car dans ce dernier cas, la View ne recevra pas la notification indiquant que la valeur a changé, car la méthode OnPropertyChanged() ne déclenchera que PropertyChangedEvent sur la propriété et non sur le terrain.

<Window x:Class="MyMVVMProject.View.HelloWorldView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyMVVMProject.View"
        xmlns:vm="clr-namespace:MyMVVMProject.ViewModel"
        mc:Ignorable="d"
        Title="HelloWorldView" Height="300" Width="300">
    <Window.DataContext>
        <vm:HelloWorldViewModel/>
    </Window.DataContext>
    <Grid>
        <Label x:Name="label" FontSize="30" Content="{Binding HelloString}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

[1] : https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx [2] : http://i.stack.imgur.com/CTF6R.png [3] : http://i.stack.imgur.com/R6mDC.png [4] : http://i.stack.imgur.com/Djj3j.png [5] : https://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx