Dot.Blog

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

Xamarin.Forms : Animer avec des propriétés attachées

Soigner les UI est indispensable pour obtenir l’adoption des utilisateurs. Les comportements par défaut des contrôles ne sont pas toujours aussi fluides qu’on le voudrait. Les animations, bien amenées et bien dosées peuvent améliorer grandement l’UI et l’UX…

Animations et MVVM

Animer des objets est assez facile à faire sous Xamarin.Forms, pas aussi direct qu’en pur XAML WPF mais la palette de moyens mis à disposition est assez large pour faire de belles choses. Quand je dit “belles” j’entends par là cette beauté particulière qui est celle d’une UI bien faite au service d’une UX de qualité, pas de beauté gratuite ou artistique.

Je ne reviendrai pas sur tous les modes d’animation qui sont largement décris au Chapitre 13 de mon livre “Animation, Transitions et Styles”, ce qui tombe bien puisque ce chapitre est offert et librement téléchargeable !

Les animations Xamarin.Forms s’effectuent par code C# plutôt que les habituels transformations disponibles en XAML classique. Ce n’est pas très gênant mais sous MVVM cela peut compliquer les choses puisque l’ordre d’animation (démarrage et arrêt) suit le plus souvent une action externe ou interne donc quelque chose qui se passe dans un ViewModel le plus souvent.

Or le ViewModel ne peut pas accéder aux éléments de l’UI… Certes le code-behind a le droit de contenir du code destiné uniquement à l’UI, mais faire dialoguer ce dernier avec un ViewModel dans le respect de MVVM complique bien les choses (utilisation de la messagerie par exemple abordées plusieurs fois dans le livre notamment page 218 pour les messages MVVM).

Comment marier les impératifs de design et ceux de MVVM ?

En utilisant des petits acteurs souvent oubliés du développeur : les propriétés attachées.

Traitées au chapitre 5, Triggers et Behaviors, les propriétés attachées sont des behaviors exposant des propriétés de dépendance classique sous XAML (mais généralement mal connues, d’où toute une section qui leur est consacrée au chapitre 4 “Le Binding” (page 141 et suivantes pour ceux qui ont le livre).

Leur intérêt est du même ordre que les convertisseurs de valeur, même si leur objectif est très différent. De façon simple on peut dire qu’il s’agit de classes définies dans l’application qui peuvent apparaître comme des propriétés nouvelles de certains objets XAML. Par exemple Grid.Row qu’on peut préciser dans un contrôle pour spécifier la ligne dans laquelle il doit se placer (lorsqu’il est placé dans une Grid) semble être temporairement une propriété du contrôle en question alors qu’il n’en est rien, c’est la Grid qui définit cette propriété “fantôme”. Transportons le contrôle hors de la Grid et “Grid.Row” n’est plus reconnu.

Je fais ici très court puisque tout cela est expliqué en long et en large et surtout en détail avec des exemples dans le livre. Mais cela devrait vous donner une idée de ce que sont des “attached behavior” ou plutôt ici “propriétés attachées”.

En quoi les propriétés attachées peuvent-elles apporter une solution ici ?

On l’a vu les animations Xamarin.Forms s’effectuent par code, or le code d’une application bien faite qui est au courant de ce qui se passe et qui pourrait agir est celui du ViewModel qui n’a pas le droit d’intervenir dans la sphère de l’UI.

L’utilisation du code-behind et de la messagerie MVVM complique les choses et ouvre en grand la porte du code spaghetti, c’est une option que je réserve pour les cas extrêmes, en dernier recours.

Reste donc les propriétés attachées…

Elles s’utilisent côté XAML, c’est du code dont on fait ce qu’on veut et qui peut agir sur l’UI, c’est un code qui peut publier des propriétés bindables, donc ouvrant la voie à une communication saine et standard avec le ViewModel.

Cela fait beaucoup d’avantages !

Une progress bar animée

Pour illustrer le propos et le rendre moins théorique partons d’une simple Progress Bar. Elle possède une propriété permettant d’indiquer le pourcentage d’avancement ce qui fait varier la longueur de la partie colorée.

C’est très bien mais les changements de valeur brusques sont immédiatement reflétés par l’affichage qui est ainsi tout aussi saccadé !

Or tout ce qui ne bouge pas de façon fluide, féline dirai-je même, fait amateur, mal fini (mais pas que !).

La ProgressBar possède bien une méthode ProgressTo bien pratique qui permet d’animer le changement de valeur. Mais qui va appeler cette méthode ? Le ViewModel ? C’est hélas impossible…

Arrivé ici certains seraient déjà partis dans le sous-classement du contrôle ProgressBar pour y bricoler la fonction d’animation. Mais ce n’est pas un bon choix. Multiplier les contrôles spécialisés pour tel ou tel cas de figure fait exploser la bibliothèque et rend l’ensemble difficilement maintenable. Imaginons juste qu’on souhaite ajouter le Touch sur la barre de progression pour faire une pause, doit-on partir de la ProgressBar d’origine ou de celle ayant le code d’animation ? Et dans un tel cas nous finirons par posséder trois ProgressBar (l’originale, celle avec animation et celle avec animation et Touch). Si on commence à développer une application en se laissant ce genre de liberté je ne donne pas cher de la maintenabilité du produit fini !

Non, je vous le dis, le mieux ce sont les propriétés attachées. On peut les spécialiser pour ne s’accrocher qu’à un seul type de contrôle ou à plusieurs et on peut cumuler leur utilisation sans jamais sous-classer un contrôle ! Ainsi on peut disposer d’un système d’animation pour la ProgressBar qui fonctionnera d’ailleurs avec tout descendant de cette dernière et créer une autre propriété attachée pour la gestion du Touch. On pourra se servir d’aucune de ces propriétés, de l’une, de l’autre ou des deux à la fois tout en laissant la ProgressBar originale inchangée… Bien entendu ce n’est qu’un exemple qui je l’espère vous donnera une idée de la richesse du procédé lorsqu’on le généralise à tous les contrôles et toutes les situations du même genre.

Regardons le résultat !

J’aime bien faire voir le résultat d’un visuel avant d’en parler, on comprend mieux où on va…

 

AnimatedProgressBar

 

Oubliez le pointeur souris que j’ai capturé par erreur. En haut une ProgressBar classique, en bas une ProgressBar tout aussi classique mais décorée par la nouvelle propriété attachée. Au milieu un contrôle de saisie autorisant les valeurs de 0 à 100 (%).

Au départ du Gif la valeur est 9, ce qui montre un petit bout de la barre de progression. Lorsque j’ajoute un 0 pour créer le nombre 90 tout naturellement la barre du haut se positionne directement proche de la fin.

Mais si vous regardez la barre du bas vous noterez qu’elle se déplace de façon fluide… et le résultat est autrement plus agréable pour l’utilisateur.

Je ne vais pas disserter des heures sur ce point de design, mais “faire joli” n’est pas le but. Il s’agit de servir l’UX, de rendre le logiciel compréhensible par l’utilisateur sans effort. Or un déplacement soudain de la barre de progression, pour peu que l’œil ne fût pas déjà braqué dessus peut passer soit inaperçu soit ne donner aucune indication (c’était dans quel état avant de bouger ?).

L’animation permet à l’œil de l’utilisateur d’attraper le mouvement, et comme il dure assez longtemps, son esprit peut suivre ce mouvement. Le changement d’état de la barre de progression devient évident, lisible. Ce changement est palpable. C’est l’esprit du Material Design de Google par exemple. Bien que le Skeuomorphisme à la Apple soit aujourd’hui totalement ringardisé et même oublié, sous l’impulsion il faut l’avouer du look "Metro" de Microsoft pour Windows 8 et Windows Phone 7/8 surtout, (encore un truc oublié !) précurseur en la matière, le “flat design” a su séduire une majorité d’utilisateurs. C'est quasi une norme d'UI aujourd'hui.
Mais un look flat très “électronique” s’il ne permet plus de faire le lien avec le monde réel des objets comme le faisait le skeuomorphisme, devient vite très inhumain, froid, incompréhensible. Tout l’esprit derrière le Material Design est de faire en sorte que bien que le look & feel soit résolument moderne (sans chercher à imiter l’aspect du monde réelà, les comportements soient eux prévisibles selon les lois de la physique du monde réel… Vous pigez la nuance ?

De fait dans le monde réel il faut du temps pour que les choses changent d’état. Si dessiner une jauge de voiture très fidèle (skeuomorphisme) n’a finalement aucun intérêt, se comporter comme l’aiguille d’une telle jauge le ferait dans la réalité est crucial ! C’est le Material Design. L’aiguille prend un certain temps à changer de position car elle possède une inertie. il s’agit donc de substituer au skeuomorphisme s’attachant à la forme d'objets réels une autre forme de réalisme qui elle s’attache à la réalité des comportements et des lois de la physique auxquels nous sommes habituées.

Animer une progress bar en dosant bien les effets ease-in et ease-out permet de s’approcher de cette fluidité du monde réel, même avec une représentation résolument électronique, flat, moderne.

Le code

Bref, j’avais dit que je ne partirai pas trop loin dans les notions de design … un peu loupé mais je pourrai en dire plus si je ne me retenais pas Smile

Donc il est temps de voir le code. Partons d’abord de celui de la propriété attachée :

(Le code est copié de Visual Studio pour conserver la mise en évidence de la syntaxe, c'est un peu plus enquiquinant à recopier, mais c'est un bon exercice !) 

image

Comme vous le constatez c’est un code assez court. Tout se trouve dans la méthode CreateAttached de la classe BindableProperty. Le code utile progressBarProgressChanged n’est guère plus long, il annule toute animation en cours et relance une autre animation pour refléter le changement de valeur. Les paramètres de ProgressTo fixent le temps de l’animation et l’easing utilisé.

Pour gérer une valeur bindable et toute l’UI d’ailleurs il nous faut un ViewModel :

image

Très rustique et n’utilisant aucune librairie MVVM particulière l’essentiel du code de ce ViewModel est de supporter INPC… Et de déclarer la propriété ProgressValue, un double, qui servira à animer la ProgressBar. La seconde propriété ProgressTextValue est celle qui sera liée au champ de saisie. Toute modification entraînera celle de la valeur liée à la  ProgressBar.

Du classique de débutant donc.

Le code-behind de la Vue se lie à son ViewModel directement sans fioriture ni librairie MVVM là non plus :

image

On remarque l’attribut de compilation du XAML en XAMLC, cela n’a rien à voir avec l’article c’est juste pour en parler …

Reste à voir comment la propriété attachée va être utilisée dans le code XAML pour donner l’effet que vous pouvez voir et revoir grâce au GIF animé plus haut.

Voici le code XAML de la page :

image

Tout se joue dans le xmlns “local” qui donne accès aux classes définies dans l’application et à l’utilisation de “local:AttachedProperties.AnimatedProgress” placé dans la balise de la ProgressBar.

C’est en cela que c’est une propriété “attachée”, elle ne peut pas s’utiliser seule et apparaît comme “attachée” à une classe existante. Attachée car elle n’en fait pas partie, c’est un code totalement indépendant. Modifiable à souhait, cumulable avec d’autres du même type, laissant l’objet de base inchangé.

Conclusion

Xamarin.Forms a réalisé un vieux rêve : un XAML universel. On sait que la prochaine étape c'est MAUI. Le rêve d’universalité en natif que nous avait fait toucher il y a longtemps du bout du doigt Silverlight n’est pas mort, XAML et C# ont su revenir par là où on ne les attendait pas, par là où Silverlight lui-même est mort : les smartphones ! Et c’est aujourd’hui toutes les plateformes de développement qui tombent comme des forteresses assiégées depuis trop longtemps. iOS, Android, UWP, même Mac OS !

Xamarin.Forms c’est le retour de la vengeance de Silverlight. MAUI sera le retour de la vengeance II de Silverlight 😊

Et ça fait toujours plaisir, même après si longtemps (les plus jeunes ne pigeront rien mais ce n'est pas grave, nous on sait que Silverlight a existé, on l'a vu !).

Stay Tuned !

blog comments powered by Disqus