MVVM simplement – 2ème partie
Après avoir démontré les limites du code-behind dans la première partie, dans cet article, l’application va mettre en oeuvre les mécanismes de binding propres à WPF.
Tout d’abord, il faut indiquer où se trouve les données qui seront liées à notre vue, c’est le rôle du DataContext, il est renseigné dans la vue et ici comme les données sont dans la classe de la vue, le lien se fait sur l’objet lui-même (par défaut, le DataContext est à null).
<Window x:Class="MvvmForDummiesPart2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Calc your BMI" Height="480" Width="525" Loaded="MainWindow_OnLoaded" DataContext="{Binding RelativeSource={RelativeSource Self}}">
En MVVM, le DataContext est assimilé au ViewModel ou modèle de la vue, c’est-à-dire tous les éléments de la vue manipulables dans le code.
Ensuite, notre liste de personnes devient une ObservableCollection, c’est-à-dire un objet qui implémente des méthodes qui notifient la vue des changements sur cette collection. Attention, l’affectation directe ne déclenche pas de notification, il convient d’utiliser les méthodes Add ou Remove.
public ObservableCollection<Person> Persons { get; set; }
Donc, la méthode LoadPersons est légèrement modifiée pour utiliser la méthode Add de la collection.
private void LoadPersons() { var xs = new XmlSerializer(typeof(ObservableCollection<Person>)); using (var rd = new StreamReader(_personFileName)) { var ps = xs.Deserialize(rd) as ObservableCollection<Person>; if (ps == null) return; Persons.Clear(); foreach (var p in ps) Persons.Add(p); } }
Il est néanmoins possible si la taille votre collection est importante de déclencher la notification suite à une affectation directe en ajoutant l’appel à OnPropertyChanged dans le setter.
private ObservableCollection<Person> _persons = new ObservableCollection<Person>(); public ObservableCollection<Person> Persons { get { return _persons; } set { if (value != _persons) { _persons = value; OnPropertyChanged("Persons"); } } }
Et dans ce cas la méthode LoadPersons évite une itération sur une collection intermédiaire.
private void LoadPersons() { var xs = new XmlSerializer(typeof(ObservableCollection<Person>)); using (var rd = new StreamReader(_personFileName)) { Persons = xs.Deserialize(rd) as ObservableCollection<Person>; } }
Du côté XAML, la proprieté ItemsSource de la DataGrid est utilisée pour lier la grille à la collection.
<DataGrid x:Name="PersonDataGrid" Grid.Column="0" Margin="20" IsReadOnly="True" ItemsSource="{Binding Persons}" SelectedItem="{Binding CurrentPerson}"></DataGrid>
La problématique de l’élément courant est résolue de façon quasi identique en liant la propriété SelectedItem de la DataGrid à une propriété dans notre code-behind. Pour ce faire, notre classe doit implémenter INotifyPropertyChanged et notifier la vue dans le setter de la propriété.
public partial class MainWindow : Window, INotifyPropertyChanged { private Person _currentPerson; public Person CurrentPerson { get { return _currentPerson; } set { if (value != _currentPerson) { _currentPerson = value; OnPropertyChanged("CurrentPerson"); } } }
Dernière étape, notre application doit calculer l’IMC, le résultat de ce calcul doit s’afficher dans un TextBlock de la vue. Pour informer la vue qu’elle doit afficher le résultat, on utilise à nouveau INotifyPropertyChanged pour la propriété Bmi.
public class Person : INotifyPropertyChanged { private double _bmi; public string FirstName { get; set; } public string LastName { get; set; } public double Height { get; set; } public double Weight { get; set; } public double Bmi { get { return _bmi; } set { if (value != _bmi) { _bmi = value; OnPropertyChanged("Bmi"); } } }
Du côté XAML, le binding est en Mode OneWay, ce qui signifie que le binding est unidirectionnel de la source vers la vue. En effet, cette zone est en lecture seule dans la vue.
<TextBlock x:Name="Bmi" Text="{Binding CurrentPerson.Bmi, Mode=OneWay}"></TextBlock>
Et voilà, le binding est mis en place et si vous observez le code en détail, vous constaterez que le code ne manipule que des objets indépendants de la vue. Le premier pas vers MVMM est donc effectué. Le code est disponible ici.
Leave a Reply