This article has been localized into French by the community.
Répondre aux changements
Jusqu'ici dans ce tutoriel, nous avons principalement créé des bindings entres des éléments d'interface utilisateur et des classes existantes. Mais dans de vrais applications, vous allez évidemment créer des binding vers vos propres objets de données. C'est aussi simple, mais dès que vous aurez commencé à le faire, vous aller découvrir quelque chose qui va vous décevoir. Les changements ne se reflètent pas automatiquement, comme c'était le cas dans les exemples précédents. Comme vous l'apprendrez dans cet article, il faut juste un peu de travail supplémentaire pour que cela fonctionne. Mais heureusement, WPF rend les choses plutôt faciles.
Répondre aux changements de source de données
Il y a deux cas de figure possibles que vous pourriez vouloir gérer ou ne pas gérer en traitant les changements de source de données. Des changements d'une liste d’éléments et des changements de propriétés liées dans chaque objet de données. La manière de les gérer peut varier, dépendant de ce que vous faites et de ce que vous cherchez à faire. Mais WPF propose deux solutions très simples que vous pouvez utiliser : la classe ObservableCollection et l'interface INotifyPropertyChanged.
Le prochain exemple va vous montrer pourquoi nous avons besoin de ces deux choses :
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="150" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private List<User> users = new List<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User
{
public string Name { get; set; }
}
}
Essayez de l'exécuter vous-même et observez comment, même si vous ajoutez un élément à la liste ou que vous changez le nom d'un des utilisateurs, rien n'est mis à jour dans l'interface utilisateur. L'exemple est très simple, avec une classe User qui stocke le nom de l'utilisateur, une ListBox pour les afficher à l'intérieur et quelques boutons pour manipuler la liste et son contenu. La propriété ItemsSource de la liste est assignée avec une liste rapide de deux utilisateurs que nous créons dans le constructeur de la fenêtre. Le problème est qu'aucun des boutons ne semble fonctionner. Réglons ça en deux étapes faciles.
Répondre aux changement de la liste source de données
La première étape est de faire en sorte que l'interface utilisateur réponde aux changements de source à l'intérieur de la liste (ItemsSource), comme quand on ajoute ou qu'on supprime un utilisateur. Ce dont nous avons besoin, c'est une liste qui prévient n'importe quelle destination des changements de son contenu. Et heureusement, WPF nous fournit un type de liste qui fait justement ça. Elle s'appelle ObservableCollection et elle s'utilise de façon similaire une List<T> normale avec seulement quelques petites différences.
Dans l'exemple final, que vous trouverez ci-dessous, nous avons simplement remplacé la List<User> par une ObservableCollection<User>. C'est tout ce qu'il faut ! Cela fait en sorte que les boutons Add et Delete fonctionnent, mais ça ne va rien changer pour le bouton "Change name" parce que le changement va se produire sur l'object de donnée lié lui-même et pas sur la source de la liste. La seconde étape consistera à gérer ce cas de figure.
Répondre aux changements des objets de données
La seconde étape est de faire en sorte que notre classe User implémente l'interface INotifyPropertyChanged. En faisant ça, nos objets User vont être capables d'avertir l'interface utilisateur des changements de ses propriétés. C'est un peu plus laborieux que de juste changer le type de liste, comme nous avons fait plus haut, mais cela reste une des manières les plus simples d'accomplir ces mises à jour automatiques.
L'exemple final et fonctionnel
Avec les deux modifications décrites plus haut, nous avons maintenant un exemple qui VA refléter les changements de la source de donnée. Ça ressemble à ceci :
<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ChangeNotificationSample" Height="135" Width="300">
<DockPanel Margin="10">
<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
</StackPanel>
<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfTutorialSamples.DataBinding
{
public partial class ChangeNotificationSample : Window
{
private ObservableCollection<User> users = new ObservableCollection<User>();
public ChangeNotificationSample()
{
InitializeComponent();
users.Add(new User() { Name = "John Doe" });
users.Add(new User() { Name = "Jane Doe" });
lbUsers.ItemsSource = users;
}
private void btnAddUser_Click(object sender, RoutedEventArgs e)
{
users.Add(new User() { Name = "New user" });
}
private void btnChangeUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
(lbUsers.SelectedItem as User).Name = "Random Name";
}
private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
{
if(lbUsers.SelectedItem != null)
users.Remove(lbUsers.SelectedItem as User);
}
}
public class User : INotifyPropertyChanged
{
private string name;
public string Name {
get { return this.name; }
set
{
if(this.name != value)
{
this.name = value;
this.NotifyPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Résumé
Comme vous pouvez le voir, implémenter l'interface INotifyPropertyChanged est plutôt simple, mais ça créée un peu de code supplémentaire dans vos classes et ça ajoute un peu de logique supplémentaire sur vos propriétés. C'est le prix que vous devrez payer si vous voulez des binding vers vos propres classes et que les changements se reflètent dans l'interface utilisateur immédiatement. Évidemment vous devez appeler NotifyPropertyChanged uniquement dans le setter des propriétés pour lesquelles vous utilisez le binding, les autres peuvent rester comme elles sont.
La collection ObservableCollection d'un autre côté est très facile à utiliser. Vous avez simplement besoin d'utiliser cette liste spécifique dans les situations où vous voulez que des changements de la source de la liste se reflètent dans un binding de destination.