I. Avant-propos - Introduction▲
I-A. Un mot sur l'auteur▲
Je suis Nathanael Marchand, diplômé 2009 de Polytech'Annecy-Chambéry. Actuellement chez So@t, j'occupe le poste d'Expert .Net qui regroupe des missions de consulting sur les technologies .Net, d'audit, de formation interne et externe, la veille technologique ainsi que la participation à la stratégie d'expertise So@t.
Vous pouvez me retrouver ici sur Developpez.com où j'anime et modère la partie .Net ainsi que sur Silverlight-France où j'ai participé à la création de cette communauté.
Vous pouvez également me retrouver sur des évènements comme les Tech'Days où j'anime des sessions (PAR305).
I-B. Introduction▲
Binding se traduit en français par le mot « liaison », il permet donc de créer une liaison entre deux éléments. On s'en sert notamment pour lier l'interface graphique aux objets qu'elle doit représenter. Ainsi du côté de l'interface graphique, je dessine un modèle d'affichage et lorsque j'applique la liaison, les champs sont remplis avec les données.
II. Le Binding, premier aperçu▲
Voici le code d'une application simple utilisant le binding, nous allons voir le fonctionnement.
using
System;
using
System.
Collections.
Generic;
namespace
BindingTutorial.
WpfApplication
{
public
partial
class
MainWindow
{
private
string
_myText;
private
List<
string
>
_myItems;
public
String MyText
{
get
{
Trace.
WriteLine
(
"Get MyText"
);
return
_myText;
}
set
{
Trace.
WriteLine
(
"Set MyText"
);
_myText =
value
;
}
}
public
List<
String>
MyItems
{
get
{
Trace.
WriteLine
(
"Get MyItems"
);
return
_myItems;
}
set
{
Trace.
WriteLine
(
"Set MyItems"
);
_myItems =
value
;
}
}
public
MainWindow
(
)
{
MyText =
"Hello world!"
;
MyItems =
new
List<
string
>
{
"Bonjour"
,
"le"
,
"monde"
};
InitializeComponent
(
);
DataContext =
this
;
}
}
}
<Window
x
:
Class
=
"BindingTutorial.WpfApplication.MainWindow"
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
Title
=
"MainWindow"
Height
=
"350"
Width
=
"525"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
Text
=
"{Binding Path=MyText}"
/>
<ListBox Grid.
Row
=
"1"
Height
=
"100"
ItemsSource
=
"{Binding Path=MyItems}"
/>
</Grid>
</Window>
Du point de vue C#, on peut voir plusieurs choses : tout d'abord on expose deux propriétés (une chaîne de caractères et une collection) ensuite on s'aperçoit qu'on initialise le DataContext de la fenêtre à elle-même. C'est là qu'intervient la magie : ce DataContext est ce à quoi est liée la fenêtre. Ainsi dans un annuaire, on aurait chaque zone qui aurait pour contexte la personne dont il est question.
Côté XAML, on peut voir le binding dans la TextBox et dans la ListBox. La syntaxe est le mot Binding ainsi que ses paramètres entre accolades. Notre contexte de données (DataContext) est la fenêtre, ce qui fait que pour s'attacher à la propriété MyText de la fenêtre, WPF doit emprunter le chemin Path=MyText. Même chose pour la ListBox qui elle à besoin d'une source qui implémente l'interface IEnumerable.
Lorsque l'on lance l'application et que l'on tape du texte dans la TextBox, on s'aperçoit qu'à la perte du focus, on a le setter de la propriété MyText qui est appelé pour mettre la nouvelle valeur. On voit donc que le Binding est à double sens : l'interface peut lire, mais également écrire.
Complexifions un peu la chose en ajoutant un bouton. Ce bouton nous allons le brancher de manière à ce qu'il change la valeur de la TextBox.
<Window
x
:
Class
=
"BindingTutorial.WpfApplication.MainWindow"
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns
:
x
=
"http://schemas.microsoft.com/winfx/2006/xaml"
Title
=
"MainWindow"
Height
=
"350"
Width
=
"525"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition
Height
=
"Auto"
/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
Text
=
"{Binding MyText}"
/>
<Button Grid.
Row
=
"1"
Content
=
"Click Me"
Click
=
"ChangeTextBox"
/>
<ListBox Grid.
Row
=
"2"
Height
=
"100"
ItemsSource
=
"{Binding MyItems}"
/>
<Button Grid.
Row
=
"3"
Content
=
"Click Me"
Click
=
"ChangeListBox"
/>
</Grid>
</Window>
private
void
ChangeTextBox
(
object
sender,
RoutedEventArgs e)
{
MyText =
MyText +
MyText;
}
private
void
ChangeListBox
(
object
sender,
RoutedEventArgs e)
{
MyItems.
Add
(
DateTime.
Now.
ToString
(
));
}
Si on clique sur le premier bouton, on s'aperçoit à l'aide des outils de Debug que le texte est bien modifié, mais que l'interface ne détecte pas ce changement. C'est un comportement voulu: pour que le binding fonctionne, il faut que l'objet observé prévienne les observateurs en cas de changement.
Sur le second bouton, c'est le même principe : bien qu'on ne remplace pas la collection par une autre collection, on change le contenu de la collection. Il faut que celle-ci avertisse ceux qui la scrutent.
Ce sont ces mécanismes de notifications que nous allons étudier dans la seconde partie.