Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Xamarin.Forms : Gérer le groupage dans les ListView

Présenter des informations groupées et facilement accessibles pour l’utilisateur est un passage forcé dans de nombreuses Apps. Mais la façon de grouper les données et de les afficher dans l’UI n’est pas très évident. Pourtant c’est assez simple, voyons comment procéder …

Le projet exemple

Posons tout de suite le contenu du projet exemple :

  • Une MainView
  • Un MainViewModel
  • Un Model contenant
    • Une classe Phone
    • Un Dataprovider
    • Une classe gérant un groupe de Phone

Le tout aidé d’un Mvvm Helper qui n’est pas vraiment utile dans cet exemple où l’INPC ne sera pas utilisé (vous pouvez donc zapper ou utilise le toolkit Mvvm que vous voulez).

La Mainview

Elle affiche une simple ListView en pleine page. Pas de fioriture.

Le MainViewModel

Il expose une seule propriété, la liste groupée, ce que nous allons voir plus bas.

Le Model

Il expose une classe Phone très simple, le nom du fabriquant, celui du modèle de téléphone et un prix :

image


Une classe gérant les groupes d’entité :

image

Rien de bien savant : cette classe descend d’une collection observable générique se spécialisant sur la classe Phone du modèle. On lui ajoute deux propriétés, la clé et la clé courte.

La clé sera tout simplement ici le nom du fabriquant, la liste contiendra tous les téléphones de ce dernier, et la clé courte permettra d’afficher une Jump List (ou index) sur les OS le supportant (UWP, iOS par exemple).

Enfin la classe fournisseur de données :

image

Cette classe statique fournit une propriété retournant tous les téléphones (Phones) ainsi qu’une méthode retournant la liste des groupes. Il s’agit d’une méthode et non d’une propriété car nous ne souhaitons pas que la génération de la liste des groupes s’effectue à chaque fois que la propriété serait invoquée (imaginons une base de données réelle en dessous les performances seraient abominables). En proposant une méthode on oblige le développeur à se créer une variable pour stocker le résultat d’un appel (donc d’une seule évaluation).

Pour Phones s’agissant d’un exemple sans base de données ni accès au réseau, la liste est une propriété car elle est initialisée une seule fois à l’instanciation de l’App. Le code ci-dessus cache les nombreuses lignes remplissant la liste de la façon suivante :

image

Le mécanisme de groupage

Pour qu’une ListView puisse afficher des données groupées il faut deux choses :

  • Fabriquer une structure de données gigogne adaptée
  • Appliquer des valeurs à certaines propriété de la ListView

La structure de données particulières

Sans mystère on devine cette structure dans la simple présentation du code du Modèle ci-dessus. Au départ nous disposons d’une liste de Phone, cela pourrait être n’importe quoi on l’a compris, puis nous avons une classe PhoneGroup qui descend d’une Collection observable et qui va contenir une liste de Phone semblables par leur clé. Celle-ci pouvant être n’importe quoi aussi (une propriété existante dans la classe Phone ou une construction dynamique par calcul par exemple).

Dans l’exemple j’ai choisi d’utiliser le nom du fabriquant comme clé. Toutefois PhoneGroup expose juste une propriété Key (et ShortKey) qui a l’avantage de pouvoir signifier tout ce qu’on veut. Demain si je veux offrir un groupage par tranche de prix par exemple je n’aurai pas à le faire en utilisant une propriété clé appelée “Manufacturer” ce qui serait une horreur de la programmation pas même spaghetti mais tout simplement paresseuse et mal conçue… Quant à créer une nouvelle classe PhoneGroup qui offrirait une clé spécialisée pour le prix puis une autre pour le fabriquant, que dire sinon DRY (Don’t Repeat Yourself) !

Bref pour afficher des listes groupées, il faut … une liste de … groupes !

Ces groupes seront ainsi des PhoneGroup, en tant que Collection PhoneGroup contiendra directement les données d’un groupe, en tant que nouvelle classe elle exposera la clé du groupe (Key).

Certains OS sont capables d’afficher une Jump List ou Index, par exemple une liste alphabétique sur le côté droit, ou dans UWP une page de tuiles indiquant les entêtes de groupe pour faciliter le déplacement rapide au sein de grandes listes de groupes. Pour UWP on peut facilement monter à 2 ou 3 caractères ce qui permet d’avoir des groupes assez parlants, pour iOS il ne faut qu’une lettre ou un chiffre. Android via les Xamarin.Forms ne propose rien de particulier il faudra donc construire une UI particulière si on souhaite afficher un index. Notre exemple ne le traite pas. En revanche pour les OS le supportant la classe PhoneGroup expose bien une propriété ShortKey qui pourra être exploitée pour afficher la Jump List.

la structure de donnée finale consiste donc en une collection de PhoneGroup, donc une Collection de Collections.

Pour fabriquer la structure en fonction d’un critère de groupement particulier la classe DataProvider expose la méthode GetPhonesByManufacturer qui, on le comprend facilement à son nom retournera une liste de PhoneGroup dont chacun contiendra tous les modèles de téléphone d’un fabriquant donné, le nom de ce dernier étant stocké dans Key du PhoneGroup.

Les propriétés spéciales de ListView

Nous disposons désormais de données convenablement groupées et exposant les clés des groupes. Mais encore faut-il afficher ces groupes…

C’est ici que la ListView entre en scène. Ce contrôle particulier est la star des contrôles en environnement mobile où les listes sont présentes partout. On le connaît bien, on sait qu’il peut afficher des listes de n’importe quoi pourvu qu’on lui indique une source d’item (une collection) et qu’on lui fournisse un DataTemplate pour l’affichage de chaque ligne.

Rien ne va changer ici. Mais on va utiliser de nouvelles propriétés spécifiques à 'l’affichage des groupes.

La première et la plus simple à comprendre (toujours grâce à l’usage de noms explicite et non ambigus) est IsGroupingEnabled qui doit être passée à True. C’est par ce biais que la ListView comprend que la source de données n’est plus une collection simple mais une collection de collections.

la ListView a aussi besoin d’une autre information, le binding vers la propriété qui permet d’afficher le nom du groupe. Pour cela on doit initialiser la propriété GroupDisplayBinding qui sera tout simplement ici un binding sur la propriété Key des PhoneGroup.

Pour activer l’affichage de la Jump List lorsqu’elle est supportée, nous indiquerons aussi le binding de GroupShortNameBinding, ici il pointera sur ShortKey de PhoneGroup. Cela est optionnel, le groupage fonctionne même si on n’affecte rien à cette propriété.

Et puis… rien, c’est tout !

Le reste est histoire d’affiche, donc de la fabrication du DataTemplate des lignes et éventuellement celui de GroupHeader pour moduler la façon dont le nom du groupe d’affiche dans la liste.

Jouer sur l’UI n’étant pas le sujet de ce billet je vous laisse peaufiner cela en fonction de votre App.

Le résultat

Sous Android nous aurons un affichage du type suivant (sans Jump List donc) :

Screenshot_1550365935

Sous UWP nous verrons plutôt :

2019-02-17 02_10_56-XfGrouping.UWP

Ce qui est très ressemblant (surtout lorsqu’on réduit la fenêtre pour obtenir des proportions de types smartphone !). Mais en cliquant sur un nom de groupe et grâce à la clé courte UWP fabriquera automatiquement une Jump List pour naviguer plus vite dans la liste :

2019-02-18 14_42_50-XfGrouping (Exécution) - Microsoft Visual Studio

Comme vous le constatez pour UWP j’ai choisi une clé de 3 lettres, c’est assez confortable pour l’utilisateur.

La fonction qui retourne la ShortKey dans PhoneGroupe peut être adaptée avec un OnPlatform pour retourner 1 lettre pour iOS et 3 pour UWP par exemple, le code exemple ne montre pas une telle adaptation, à vous de jouer !

Le code source

Vous le retrouver sous le nom de XfGrouping.zip dans le repository de Dot.Vlog à cette adresse : http://tiny.cc/DotVlog

Si le lien ne fonctionne pas utiliser le lien long : https://www.dropbox.com/sh/0n1whngngn15lps/AABGZaMDOHerCf-7EkkHW6w-a?dl=0

Conclusion

Grouper n’est pas afficher… fabriquer une structure de données pour l’affichage de listes groupées est très simple (une collection de collections). Afficher cela de façon attrayante sous tous les OS est le job du designer aidé du développeur. Template selectors, adaptations personnalisées à chaque plateformes, animations Lottie, gestion de la recherche dans la liste, etc, tout cela réclame pas mal de travail pour arriver à une App finie et déployable.

Mais rien n’est possible tant qu’on ne sait pas comment utiliser la ListView pour afficher les groupes !

J’espère vous avoir ici éclairé concernant ce point technique…

Stay Tuned !

blog comments powered by Disqus