Comme je vous l’avez écrit dans mon précédent article, quelques prérequis doivent être connus afin de continuer à bien notre quête. En effet, le chemin sera long avant de maîtriser tous les tenants et aboutissants de cette matière vaste. Selon l’adage, “petit train ira loin”, je m’efforcerai le plus possible à rentrer dans les détails, quitte parfois à sortir de notre but initial. Prenons par exemple cet article, il traîte du databinding, bien qu’il se détourne de notre but (Composite Application Guidance), il pourra néanmoins vous servir pour comprendre le principe du binding dans wpf et silverlight (ce qui pourra vous servir pour d’autres projets). Et finalement, ce n’est pas cela être Agile ? Se développer de manière indépendante et ensuire recoller les morceaux (ici l’apprentissage) afin de former un tout (la connaissance).
Qu’est ce que le databinding ?
Selon Ms, c’est la méthode qui permet de lier les composants UI (textbox, listbox,…) à la couche métier. En effet, si les données changent dans la couche métier et que le binding est correctement configuré, les éléments UI se mettront à jour automatiquement. Mais l’inverse est également vrai, si l’utilisateur rentre une valuer dans un textbox, par le binding, le changement sera automatiquement reflété dans la couche métier.
Concept du databinding :
fig 1 (from MSDN)
Par exemple, si vous vouler “binder” le contenu d’un Textbox à la propriété Nom d’un objet Humain, votre objet cible sera la TextBox,
la propriété cible sera la propriété Text de l’object Textbox, la valeur à exploité (le “path”) est la propriété source Nom de l’objet source Humain.
A noter que pour créer ces liens (ce databind), vous utilisez des BindingOjbects, c’est grâce à ces objets que le lien peut se faire.
La direction du flux de donnée :
figure 2 (from MSDN)
En effet, le flux de données à un sens, comme l’on peut le voir dans la figure 2. Il y a un sens aux flèches. Le flux de donnée va d’un object source vers un objet cible. La propriété name du textbox pourrait être tout aussi bien être la cible que la source. Ce n’est plus la propriété text du TextBox qui se “remplit” de la proriété Nom de l’objet Humain mais c’est maintenant la propriété text du TextBox qui “remplit” la propriété Nom de l’objet Humain. Ceci implique donc trois sens pour les flux de données (noms donnés en anglais).
Comment détecter des changements dans l’objet source (ceci uniquement pour OneWay et TwoWay) ?
L’objet doit implémenter un méchanisme de notification, en implémentant l’interface INotifyPropertyChange (nous verrons pourquoi plus tard).
Que déclenche un changement dans la source ?
Les databindings qui sont TwoWay et OneWayToSource doivent être attentif aux changements de la propriété cible afin de les propagers vers l’objet source. Le sens du flux de données est assuré par la propriété Mode de l’objet Binding.
Cependant, est-ce que la valeur de votre objet source (par exemple la propriété Nom de l’objet Humain) change t’elle lorsque vous être entrain de changer cette valeur (lors de la saisie dans le TextBox) ou après que vous ayez fini (de changer la valeur du TextBox) ?
En fait, c’est la propriété UpdateSourceTrigger qui détermine ce qui va déclencher la mise à jour de l’objet source.
figure 3 (from MSDN)
Si nous regardons la figure 3, les points représentés aux extrémités des flèches illustrent le rôle de l’UpdateSourceTrigger.
Si l’UpdateSourceTrigger à la valeur “PropertyChanged”, dès que la valeur dans le textbox changera, la valeur de l’object source changera également. Par contre, si l’UpdateSourceTrigger à comme valeur “LostFocus”, la valeur de l’objet source sera propagée lorsque nous nous retirerons du TextBox (passer à un autre textbox par ex.). La valeur par défaut de UpdateSourceTrigger dépend de l’objet. Pour un TextBox cette valeur est, par défaut, LostFocus. En effet, si nous devrions propager la valeur à chaque fois que nous entrons une lettre dans le textbox, les performances diminueraient, c’est pour cette raison que la valeur par défaut de UpdateSourceTrigger est LostFocus et non pas PorpertyChanged.
Il existe également pour le textbox une troisième valeur pour UpdateSourceTrigger, Explicit, utilisée par exemple pour mettre à jour la valeur seulement lors d’un clique de bouton par l’utilisateur (plus d’info sur l’UpdateSourceTrigger : ici ).
Créer un Binding :
Passons au pratique, nous allons établir un lien à l’aide de l’objet binding. Je ne vais pas vous le cacher, pour commencer je vais prendre un exemple MSDN.
Nous avons un object source qui est une classe MyData, elle a une propriété string ColorName dont la valeur est rouge. Nous voulons lier la couleur du background à la valeur ColorName de MyData.
Voici le code en XAML : pour plus d’info sur la syntaxt du binding (ici)

Et voici le code C# :

Ce binding est un binding OneWay en effet, la propriété background du TextBox est par défaut OneWay.
figure 4
Le background property de l’object textbox est de type brush, néanmoins nous lui passons un string, ceci fonctionne par une conversion implicite (plus d’info ici).
Spécifier le Binding Source :
Ici le binding source est défini spécifiant une valeur au DataContext. Le DataContext du Window1 est l’objet MyData. TextBoxt hérite de ce datacontext par l’objet Window1 (son parent). En donnant le chemin vers la propriété qui doit être lié dans le background du textblock. Celui-ci sera où prendre ces données. Si nous ne définissons aucune valeur pour le datacontext, le background du textbox ne prendrait pas la couleur rouge.
Nous obtenons alors le résultat suivant :

Il est intéressant de définir le datacontext dans un niveau parent lorsque vous voulez utilisez ce contexte pour plusieurs enfants. Dans le cas contraire, vous pouvez définir ce contexte rien que pour l’élément que vous voulez lier.
La Classe BindingExpression :
Comme défini plus haut, Binding class est la classe haut niveau pour définir un Binding. Le BindingExpression qui est une sous-jacente de l’objet Binding, cet objet permet de maintenir la connexion entre l’objet source et l’objet target. Un objet Binding contient toute l’information et peut être partagée par plusieurs BindingExpression. Une BindingExpression est une expression d’instance qui ne peut partager ses informations, elle contient toute l’information de l’instance du Binding.

myDataObject est une instance de la classe MyData, myBinding est la source de l’objet Binding, MyData est une classe qui contient la propriété string MyDataProperty. Cet exemple lie le contenu text d’une instance de TextBLock myText vers MyDataProperty.
Dans ce cas, vous pouvez réutiliser l’objet myBinding, par exemple, en liant à un propriété d’un TextBlock. A ce moment, il n’y aura qu’un objet Binding mais deux instances de BindingExpression qui se partage l’objet Binding.
DataConversion :
Dans l’exemple précédent, la propriété background du Texbox est lié à une propriété string avec la valeur « Red ». Cela fonctionne car le type Brush est capable de convertir un string en un objet Brush .
Comme l’on peut remarquer sur la figure 5, le Type Brush à une conversion par défaut qui permet de convertir un objet de type string en objet de type Brush.
Que se passerait t’il si notre objet source avait un type Color pour la propriété ColorName. Nous devrions implémenter l’interface IValueConverter (pour plus d’info sur IValueConverter, cela se passe ici). De cette manière l’objet binding sait comment convertir un objet Color en objet de type SolidColorBrush.

Par l’implémentation de cette inferface, notre schéma devient alors :
figure 6
Généralement, pour les types cible connus, la conversion par défaut devrait fonctionner. En cas de doute, vous devriez implémenter l’interface IValueConverter.
Voici une liste de cas où l’utilisation de cette interface est préconisée :
Binding to Collection :
Dans un Binding, un objet source peut être soit traité comme un simple objet dans lequel ses propriétés contiennent des données soit comme une collection de données d’objets polymorphiques groupés ensembles (comme le résultat d’une query provenant d’une base de données). Ici nous avons toujours utilisés des objets simples mais nous aurions pu utiliser des ItemsControl comme des ListBox, des ListView, des TreeView afin d’afficher des collections d’objets.
figure 6
Comme l’on peut remarquer dans le diagramme précédent, pour lié un ItemsControl à une collection d’objet, nous devons utiliser la propriété ItemsSource. ItemsSource est en quelque sorte le contenu du ItemsControl. Par défaut la direction du flux de donnée pour un ItemsControl sera OneWay.
Comment implémenter les collections ?
Vous pouvez utiliser n’importe quelles collections qui utilisent l’interface IEnumerable. Pour gérer un binding qui permettrait l’insertion, la suppression ou l’update de donnée automatiquement retranscrite dans l’UI, la collection doit implémenter l’interface INotifyCollectionChange. Cette interface permet d’exposer un évênement qui sera déclenché à chaque changement dans la collection.
WPF founit la classe ObservableCollection(T) qui est une implémentation d’une collection de donnée qui comprend l’interface INotifyCollectionChange. A noter que pour supporter le transfert de données entre l’objet cible et l’objet source, chaque objet dans la collection doit implémenter l’interface INotifyPropertyChange.
Avant d’utiliser votre propre collection, tentez d’utiliser ObservableCollection(T) ou l’une des classes de collection existante comme List(T), Collection(T), BindingList(T), parmis d’autres.
Collection Views
Une fois que votre ItemsControl est lié à un collection de données, vous pourriez vouloir trier, filtrer ou grouper les données. Pour faire cela, vous utilisez les collection views, qui sont des classes qui implémente l’interface ICollectionView.
Que sont les Collection Views ?
Une collection est une couche supérrieur par rapport à une “binding source collection” ce qui vous permet de naviguer et d’afficher la collection source triée, flitrée et groupée en queries, ceci sans changer la source sous-jacente elle même. Une “collection view” maintient également un pointeur vers l’item courant dans la collection. Si la collection source implémente l’interface INotifyCollectionChange, les changements seront déclenchés par l’évênement CollectionChanged qui sera propagé aux vues.
Parce ques les vues ne changent pas la collection source adjacente, chaque collection source peut avoir plusieurs vues associées à elle. Par exemple, vous pourriez avoir une collection d’objets Task. Avec l’utilisation de vues, vous pourriez afficher la même donnée sous différentes vues. Par exemple, à gauche vous afficheriez les Tasks triées par priorité et de l’autre triée par date.
Commen créer une vue ?
Une façon de créer et utiliser une vue est d’instancier les objets vues directement et les utiliser comme source pour le binding.
Comme nous voyons dans l’exmple ci-dessus qui provient de MSDN, nous pouvons voir que le ListBox à pour Object source un CollectionViewSource lui-même lié à une liste d’objet AuctionItems . Vous pourriez réutililiser ce CollectionViewSource pour un autre type de contrôle, ce qui est important ici est de ne pas oublié de définir le x:Key dans la CollectionViewSource afin de que le contrôle puisse savoir où il va chercher ses données.
Le tableau suivant nous montre quels types de données (vue) sont crées comme CollectionView par défaut (IList -> ListCollectionView). Le résultat serait le même si nous utiliserions un CollectionViewSource afin de créer les CollectionViews.

Utiliser une Default View
Spécifier une collection view comme un “binding” source est une des solutions pour créer et utiliser des collection view. WPF crée également une default collection view pour chaque collection utilisée comme source du binding. Is vous liez directement un contrôle, WPF la lie à sa vue par défaut. Cette vue par défaut est partagée par tout les “bindings” de la même collection, donc un changement fait à la vue par défaut par un contrôle lié ou du code (trier, filtrer,…) est reflétée dans tous les autres bindings de la même collection.
Collection Views avec ADO.NET DataTables
En vue d’améliorer les performances, les collection views pour ADO.NET DataTable ou les DataView délèguent le tri, les filtres au DataView. En conséquent, le tri et le filtrage sera partagé à travers toutes les collections du datasource. Afin de mettre en place le tri et le filtrage sur chaque collection view, vous devez initialiser chaque collection view avec son propre DataView object.
Sorting
Comme mentioné ci-dessus, les vues peuvent trier un collection. Comme il existe dans la collection adjacente, vos données pourraient ne pas être dans un ordre pertinant. La vue au dessous de cette collection adjacente vous permet d’imposer un ordre et de changer l’ordre par défaut, basé sur des critères de comparaison que vous définissez. Parceque c’est un client-based view des données, un scénario commun pourrait être un utisateur qui veut trier les colonnes d’un tableau suivant ses valeurs correspondantes. En utilisant les vues, ce tri personnalisé pourrait être appliqué, et ce, sans faire de changement dans la collection de la couche adjacente et sans devoir faire de query dans cette couche adjacente (plus d’info ici).
Voici un exemple de tri qui trie par Catégorie et par StartDate :

Vous pouvez trouver un exmple de binding intéressant sur MSDN, ici.
Filtering :
Une vue peut également appliquer un filtre à une collection. Cela ne permet d’afficher qu’un sous-esemble d’une collection. Vous pouvez également trier sous des conditions personnalisées.
Voici un exemple de filtre qui provient de la démo fournie par MSDN.

The ShowOnlyBargainsFilter event handler

Grouping :
Exepté pour les classe internal qui sont retranscrite en IEnumerable collection, toutes les collections views supporte la fonctionnalité de grouping. Ceci permets aux utilisateurs de subdiviser la collection dans la collection view dans un groupe logique. Les groupes peuvent être explicite, l’utilisateur fournit une liste de groups ou implitcit, les groups sont générés dynamiquement dépendamment des données.
Voici un exemple de fonction de grouping :

Pointer sur l’item actuel:
Les vues supportent également la notion d’item actuel. Vous pourriez naviguer à travers les objets dans une collection view. Pendant que vous naviguer, vous passez d’un item à un autre et un pointeur d’item vous permet de retirer les informations de l’item actuel.
Comme WPF est lié à une collection uniquement en utilisant une vue, toutes les liaisons vers les collections possèdent ce pointeur d’item. Quand vous liez à une vue le caractère “/” désigne le chemin vers la valeur actuel. Dans l’exemple suivant, le data context est une collection view, le premier binding est lié à la collection, le second binding est lié à l’item actuel de la collection et le troisième est lié à la propriété Description de l’item actuel.
Ex :
Le Master Detail Binding Scenario
La notion d’item actuel est utile pour le Master Detail Binding Scenario. Si nous regardons dans l’exemple ci-dessous où le contenu du ListBox détermine le contenu affiché dans le ContentControl. Vous pouvez déterminer le Master Detail Binding scenario en liant deux ou plus contrôle à une même vue.

Ces deux contrôles sont liés à une même vue (listingDataView). Cette exemple marchera car lorsque un objet singleton (ici ContentControl) est lié à une collection view, il est automatiquement à l’item actuel (CurrentItem) de la vue. Si le list control n’est pas lié à un objet CollectionViewSource comme dans l’exemple, alors, vous devriez définir la propriété IsSynchronizedWithCurrentItem à “true” pour que cela fonctionne. L’exemple ci-dessous ne fonctionnerait pas si il n’utiliserait pas un template (voir prochain article).
DataTemplate :
Je reviendrai dans un prochaine sur l’utilisation de DataTemplate.
En attendant, voici une série de lien qui pourra vous être utiles :
http://msdn.microsoft.com/en-us/library/ms752347.aspx#specifying_the_binding_source
http://msdn.microsoft.com/en-us/library/ms752347.aspx#data_conversion
http://msdn.microsoft.com/en-us/library/ms752347.aspx#data_conversion
http://msdn.microsoft.com/en-us/library/dd458894.aspx
http://msdn.microsoft.com/en-us/magazine/cc163299.aspx
http://odetocode.com/articles/740.aspx
1 Response to Composite Application Guidance WPF/Silverlight - Requis : le DataBinding - 3
Steven
January 14th, 2010 at 5:56 am
Bonjour,
Je suis tombé sur votre blog par hasard. Après avoir parcouru quelques articles, je l’ai immédiatement placé en favori !
J’ai particulièrement apprécié vos articles sur le Data Binding et les Routed Event. Ce que j’apprécie, c’est que les choses sont clairement expliquées (c’est plutôt rare). De plus, vous parlez également des mécanismes qui sont utilisés dérrière ces notions, ça aide fortement à la compréhension.
J’ai vraiment hâte de lire votre article sur les Dependency Property