MVVM simplement – 1ère partie

Devant la forte demande de certains ;-), j’ai décidé de montrer par le code comment une application passe du code-behind à MVVM. Je commence par 3 articles qui détaillerons cette migration.

J’ai choisi de partir d’une application WPF basique (calcul d’IMC), je montrerai dans de futurs articles comment écrire une application universelle  (Windows 8, Windows Phone) identique.

Dans le premier, je pars d’une application écrite sans utiliser les capacités de binding de WPF et encore moins MVVM.

Les données utilisées sont décrites dans une classe Person.

 public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public double Height { get; set; }
        public double Weight { get; set; }
        public double Bmi { get; set; }
    }
    

L’application utilise une liste de personnes qui est chargée dans une DataGrid.

Les données sont stockées dans un fichier xml et l’accès au fichier se fait par des méthodes LoadPersons et SavePersons.

private void SavePersons()
{
    var xs = new XmlSerializer(typeof(List<Person>));
    using (var wr = new StreamWriter(_personFileName))
    {
        xs.Serialize(wr, Persons);
    }
}
 
private void LoadPersons()
{
    var xs = new XmlSerializer(typeof(List<Person>));
    using (var rd = new StreamReader(_personFileName))
    {
        Persons = xs.Deserialize(rd) as List<Person>;
    }
}

La création, modification, suppression d’une personne se fait avec des méthodes dédiées, je vais présenter en détail la méthode CreatePerson et montrer les inconvénients du code-behind.

private void CreatePerson()
        {
            if (this.FirstName.Text != string.Empty && this.LastName.Text != string.Empty)
            {
                double weight, height, bmi;
 
                if (!double.TryParse(this.Weight.Text, out weight))
                    return;
 
                if (!double.TryParse(this.Height.Text, out height))
                    return;
 
                if(!double.TryParse(this.Bmi.Text, out bmi))
                    return;
 
                var newPerson = new Person { FirstName = this.FirstName.Text, LastName = this.LastName.Text,
                    Height = height, Weight = weight, Bmi = bmi};
                Persons.Add(newPerson);
                SyncDataGridItemsSource();
                SavePersons();
            }
        }

On peut constater que la méthode accède directement aux propriétés de l’objet Window afin de lire les données de la nouvelle personne.

Premier inconvénient: Il existe donc un couplage fort entre le code de création d’une personne et la vue.

(J’emploie dorénavant le mot “vue” qui représente la fenêtre de l’application). De plus, la différence de type entre la valeur de la TextBox et les propriétés de type double oblige à convertir les données.

Deuxième inconvénient : aucun lien dynamique n’existe entre la DataGrid et notre liste, une méthode de synchronisation “en dur” est donc nécessaire

Cette absence de lien dynamique entre la vue et les données se retrouve dans la méthode DeletePerson, j’utilise ici un index représentant l’élément sélectionné dans la DataGrid, j’obtiens l’élément directement dans la collection d’items de la DataGrid, je le supprime de la liste et je resynchronise l’affichage (J’aurais pu utiliser le SelectedItem de la DataGrid mais cela n’aurait rien changé à l’absence de liaison dynamique).

 private void DeletePerson()
        {
            if (_currentPersonIndex < 0) return;
            var person = this.PersonDataGrid.Items[_currentPersonIndex] as Person;
 
            Persons.Remove(person);
            this.FirstName.Text = string.Empty;
            this.LastName.Text = string.Empty;
            this.Weight.Text = string.Empty;
            this.Height.Text = string.Empty;
            this.Bmi.Text = string.Empty;
            SyncDataGridItemsSource();
            SavePersons();
        }
private void SyncDataGridItemsSource()
        {
            this.PersonDataGrid.ItemsSource = null;
            this.PersonDataGrid.ItemsSource = Persons;
        }

En conclusion, cette application montre les limites du code-behind sans utiliser le binding WPF : couplage fort entre la vue, les traitements, les données métier; pas de liaison dynamique entre la vue et les données. Dans un deuxième article, j’expliquerai ce que peut apporter le mécanisme de binding natif à WPF à notre application. Le source de l’application est disponible ici.

December 30, 2017

Tags: ,

Leave a Reply

Your email address will not be published. Required fields are marked *