Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Xamarin.Forms: Utiliser correctement le data binding

Le data binding est un mini langage dans le langage, il est puissant mais il ne dévoile pas tous ses secrets au premier coup d'œil. En voici quelques-uns…

Le Data Binding

Je ne vous ferai pas un long exposé sur sa nature, sa syntaxe et tout le reste, c’est un sujet que j’ai traité ici nombre de fois et qui fait l’objet d’un chapitre entier de mon livre sur les Xamarin.Forms tellement il y a à dire. Et preuve de la complexité et de la richesse du sujet je peux encore trouver d’autres choses à vous présenter aujourd’hui !

Tout ne sera pas forcément nouveau pour les lecteurs assidus de Dot.Blog ou ceux de mon livre mais il y a surement dans ce billet au moins un ou deux trucs que vous ne savez pas ou que vous avez oublié…D'où je pense l'intérêt d'en "remettre une couche" !

Compiler les data Bindings

La compilation du code XAML permet de gagner un peu de temps à l’exécution et de bénéficier d’alertes ou d’erreurs lors de la compilation ce qui rend cette option particulièrement intéressante.

Elle peut s’activer au niveau global ou page par page. Le plus simple étant de l’activer au niveau global d’un namespace, normalement celui de votre code partagé. Normalement les nouveaux templates l'active par défaut. Sinon vérifiez :

image


Mais cela n’est pas vraiment tout…

Il y a un second niveau d’astuce pour activer la compilation des bindings qui sinon restent calculés à l’exécution, notamment en MVVM où la View ne connaît pas la classe du ViewModel.

Ce niveau s’active en précisant le type de la donnée qui est bindée, ce qui semble naturel puisque s’il manque, XAML est bien obligé d’attendre l’exécution pour le connaître, et que, justement c’est ce qu’on cherche à éviter en forçant une reconnaissance précoce ! Et pour cela il faut utiliser x:DataType. Premier indice. Mais où ?

Le mieux est de le placer au plus haut niveau du code XAML, celui de la page par exemple, de telle façon à ce que toute la vue puisse en profiter. C’est à ce même niveau que se trouve l’initialisation du BindingContext.

Toutefois cela soulève un petit problème… si l’on respecte MVVM il n’est pas possible de lier la View au ViewModel, c’est mal. Je plaisante, il y a de bonnes raisons à cela que je ne développerai pas ici. Néanmoins cela ouvre débat puisque la View est conçue pour fonctionner avec des champs bien précis elle pourrait dans l’esprit connaître son ViewModel sans “trop” violer MVVM. Autant créer un lien entre un ViewModel et une View ou tout élément de l’IHM est indiscutablement une erreur, autant dans l’autre sens, on peut en discuter et je pense que cela peut se défendre (sauf dans un système idéal avec Unit Testing, mocking etc, mais franchement j’en vois ultra rarement des projets avec Unit Testing aussi peu qu'il y a 10 ans ou plus…). Mais restons fidèles à la ligne fixée par MVVM. D’ailleurs ce pattern autorise le lien direct entre une View et un élément du Model si cela est ponctuel et pour une View n’ayant aucune interaction (car une interaction impliquerait par exemple un bouton donc une commande donc du code, c’est à dire quelque chose qui doit absolument être dans un ViewModel, et ça, ça ne se discute pas).

Si on essaye d’être un peu filou, le lien View –> Model est autorisé, mais pas View –> ViewModel. Ok. On peut considérer qu’une donnée exposée par le ViewModel est issue de la couche Model (c’est même une obligation ou alors c’est un type de données .NET ce qu’on considèrera comme équivalent ici). Donc si ce n’est pas au niveau global de la page que l’on va créer un lien vers le type du ViewModel rien ne nous empêche de lier ponctuellement un élément de la View au type d’un élément du Model

Vous captez la subtilité ?

Un petit exemple va aider. Considérons que dans la couche Model on dispose d’une classe Clients.Models.Deal décrivant une transaction. Et imaginons que le ViewModel expose une liste de Deal. Comme vous le constatez je ne vous parle pas un instant ni du nom de la propriété exposée ni de la classe du ViewModel, juste du type de la donnée provenant de la couche Model.

Pour afficher la liste, la View va utiliser par exemple une ListView, le coup classique pourrait-on dire. C’est à ce niveau, là où se trouve le binding avec les éléments de la liste que nous allons injecter le type de la donnée affichée :

image

Dans l’ItemTemplate de la ListView, au niveau de son DataTemplate, on fixe le type affiché par ce dernier et le tour est joué ! XAMLC va pouvoir compiler le code XAML mais il va aussi pouvoir compiler le Binding puisqu’il connaît à l’avance le type qu’il aura à traiter !

C’est vraiment une bonne astuce car on a le meilleur des deux mondes, celui du early binding permettant à la compilation de faire son œuvre (notamment si la donnée affichée supporte INPC le compilateur effectuera bien le lien sur l’événement aussi), et celui du late binding au niveau du ViewModel ce qui permet de respecter totalement MVVM. La liaison View->Model étant autorisée.

Parallèlement vous aurez noté que le namespace XML (xmlns) fonctionne ici en utilisant “using” et non “clr-namespace”. Cela n’a rien de spécifique au binding compilé c’est juste une autre astuce XAML à connaître… Lorsque la référence est dans le même assembly cela ne sert à rien de se compliquer la vie avec un “clr-namespace”, un simple “using” est suffisant !

On note dans le code exemple que la ListView affiche les données via un contrôle personnalisé, une ViewCell dont le contenu est issu d’un ContentView. Inutile de préciser (si ? alors je le fais !) qu’on peut utiliser l’astuce du x:DataType à n’importe quel niveau de la hiérarchie, donc aussi à l’intérieur du ContentView. On s’assure ainsi une compilation totale et sans ambiguïté.

Les autres astuces

Il existe bien d’autres subtilités à connaître. L’utilisation du StringFormat par exemple ou des binding OneWayToSource en font partie, mais soit j’en ai déjà parlé, soit j’en parle dans mon livre, soit je vous en reparlerai car il faut en laisser pour plus tard !

Plus loin on peut aussi pousser le concept à son extrémité et ajouter un x:DataType sur le ViewModel dans l'entête de la Page... Les puristes vont crier, les pragmatiques applaudiront. A chacun de choisir, je propose, c'est vous qui décidez !

Conclusion

Le typage des bindings dans la View doublé de la compilation XAML permet à XAMLC de générer un meilleur code, plus rapide à charger, et mieux débogué à la compilation. On peut bénéficier de tout cela sans violer les règles de MVVM, non pas qu’ils s’agissent de lois quasi divines mais parce qu’il s’agit de règles de bon sens pour créer de bons logiciels testables.

Toutes les règles peuvent se contourner si le contexte le justifie, mais bien souvent si on réfléchit un peu plus on peut trouver des solutions qui tout en respectant MVVM permettent de bénéficier des petits avantages qu’une violation de la règle laissait espérer.

Et en agissant ainsi à chaque fois on se sert de MVVM non pas comme d’un texte sacré mais comme d’une éthique nous permettant de rester sur la voie. S’en écarter trop souvent, être trop permissif, c’est à coup sûr entasser au fil du temps tellement d’acrobaties que tout se cassera la figure.

Ici nous avons vu comment marier la compilation précoce de XAML avec les règles de MVVM. Produire un meilleur code sans faire de sacrifices…

Stay Tuned !

blog comments powered by Disqus