Ajouter des éléments spéciaux dans une collection liée via Binding

Le Binding des technologies basées sur XAML (Silverlight / WPF) a constitué une grande avancée pour simplifier le découplage des données et de l’interface en apportant un contrôle poussé des données affichées dans la vue ainsi qu’une grande simplicité. Cependant certains rares cas de figures étaient plus faciles à implémenter avec les mécanismes de Binding pré-XAML.

Cet article présente l’un de ces cas, et une astuce pour le contourner, transparente pour l’utilisateur. La méthode exposée en est assurément une parmi tant d’autre mais je n’ai que trouvé peu de solutions sur la toile (sans doute me manquait-il les mots-clés magiques). Je vous présente donc la mienne, possiblement améliorable, et si vous avez une autre technique à ce même problème, n’hésitez pas à réagir via les commentaires !

Le problème

Nous sommes dans une application de test de gestion d’une bibliothèque (vous savez ces endroits où l’on peut trouver des ensembles de feuilles de papiers attachées les unes aux autres appelés « Livres »… Qu’importe c’est un exemple théorique). La vue permettant d’ajouter un nouveau livre comporte trois champs, dont une catégorie qu’il faut sélectionner dans une liste de choix.

Cette liste de choix, est dans cet exemple implémenté avec le ListPicker du Silverlight Toolkit pour Windows Phone. On a lié sa propriété ItemsSource à une collection Categories contenant les objets représentant les catégories, possiblement chargée elle-même depuis une base de données.

<toolkit:ListPicker ItemsSource=”{Binding Categories}” />

Seulement voilà, vous voulez gérer le cas où un livre n’a pas la catégorie proposée ! Quelles sont alors vos possibilités ?

  • Ajouter un enregistrement dans la base nommé « Aucune » est une solution peu intelligente car on serait en train de mélanger des catégories avec des non-catégories !
  • Mettre le SelectedIndex du ListPicker à -1 ; quand on ouvre la vue, il n’y a donc pas de catégorie de sélectionner. Cela fonctionne, mais si l’utilisateur choisi une catégorie, il ne pourra plus se rétracter, et sera condamné à fermer la vue et à la rouvrir ! Pas très ergonomique…
  • Ma solution : utiliser une classe pour étendre la classe utilisée dans la collection pour fournir des items spéciaux qui représentent des valeurs non liées. Tout un programme !

La solution

Un enum qui représente les types d’éléments spéciaux

Pour symboliser les éléments spéciaux nous allons créer tout d’abord un enum qui correspond à leurs types. Par exemple ici, j’ai mis les types suivants : aucun, tous et autre. Vous êtes libre d’en ajouter ou d’en enlever, ma solution est plus un pattern qu’un framework, et il vous faudra sans doute l’adapter à vos besoins.

La classe pour étendre les entités

Cette classe implémente le Design Pattern Decorator qui consiste à encapsuler un objet dans un autre objet pour lui ajouter des fonctionnalités. Ici on rajoute un attribut de type SpecialItemType qui est l’enum que nous avons déclaré plus tôt (c’est un Nullable pour pouvoir gérer le cas où l’élément n’a pas de type spécial).

A noter également que ma classe dérive de NotificationObject qui est apportée par Prism v4 pour Windows Phone (sur lequel je ferai un article plus complet plus tard). Globalement il sert uniquement à implémenter l’interface INotifyPropertyChanged et définit une méthode RaisePropertyChanged que l’on peut utiliser dans les setters de ses classes. Nous pouvons à présent utiliser cette classe dans la collection de catégories située dans notre ViewModel.

L’affichage des catégories et des non-catégories

Pour l’affichage nous sommes confrontés à un dernier problème : un élément à afficher de deux façons différentes selon le type d’objet. Heureusement ce problème-là, je l’avais déjà traité dans mon post : https://sebastienmornas.wordpress.com/2010/10/07/utiliser-un-datatemplateselector-en-silverlight/

Il faut créer un DataTemplateSelector qui choisit son template en fonction du caractère spécial de l’élément ou non. Pas de surprise dans mon implémentation, si ce n’est que cette fois j’utilise la classe DataTemplateSelector définie par Prism v4 (aucune différence avec la version que j’avais présentée dans mon article, mais au moins il n’y a plus besoin de la réécrire dans chaque projet !)

Enfin dans notre XAML, il ne nous reste plus qu’à utiliser ce DataTemplateSelector, comme ceci :

Et voilà : visuellement, on a bien ce qu’on s’attend à voir, et sous le capot, on voit que la catégorie est à null ce qui représente bien la notion de « aucune catégorie ». Certes la solution n’est pas immédiate à mettre en place, mais elle a le mérite de garder une architecture claire et propre et est très facilement adaptable et personnalisable.

Publicités