Dot.Blog

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

Silverlight / WPF : Style ou Template ?

[new:04/06/2010]Durant mes formations je m’aperçois que certaines notions qui, avec l’habitude me semblent “basiques” ont parfois plus de mal à être saisies que d’autres. Ainsi en est-il de la nuance entre Styles et Templates.

Il me semble donc important de clarifier les choses car si les deux concepts sont proches ils n’en sont pas moins différents et servent des objectifs tout aussi différents.

Les Templates

Commençons par eux. Les Templates permettent de modifier la forme d’un composant visuel. C’est grâce à un Template que vous pouvez rendre un bouton rond, un radio bouton rectangulaire.

Le Template s’intéresse à la forme.

Les Styles

Les styles sont des collections de couples propriété / valeur.

C’est dans un style qu’on fixera la couleur de la fonte, sa taille, la couleur du fond, etc, pour qu’un contrôle visuel corresponde à la charte graphique de l’application. Un Style ne peut pas être utilisé pour rendre un bouton rond, mais il doit être utilisé pour le rendre bleu (ou vert ou ce que vous voulez).

Un style s’intéresse à l’aspect final.

Pourquoi cette confusion entre styles et templates ?

Elle ne naît pas de rien. Les choses peuvent être confuses car si on s’en tenait à ce que je viens de dire les choses seraient certainement plus claires (quoi que… la différence entre “forme” et “aspect final” semble un peu spécieuse il faut l’avouer).

Un Style peut exister sans Template. L’inverse est vrai. Mais un Style peut contenir un Template, l’inverse n’étant pas vrai. En effet, dans un objet le Template est représenté comme un propriété tout à fait ordinaire. Un Style peut donc parfaitement, dans sa liste de couples propriété / valeur, fixer la valeur de la propriété Template…

Pourquoi encapsuler le Template dans un Style ?

Il y a de bonnes raisons de faire de la sorte, regardons de plus près justement la séparation des tâches entre Templates et Styles…

Un Template dans un Style

Vous allez très vite comprendre (enfin je l’espère !). Supposons que nous voulions créer un bouton rond, histoire d’être original (je plaisante…). Comme nous sommes des développeurs (ou des designers) professionnels, nous sommes constamment aiguillonnés par l’envie de faire bien et surtout de faire réutilisable (un bon développeur est un fainéant,  ceux qui n’ont pas cette immense qualité sont voués à être des pisseurs de lignes toute leur vie).

Ainsi, bien qu’il faille un bouton rond et bleu pour notre application, nous allons réaliser un bouton “générique” qui pourra être aussi bien bleu que vert ou jaune.

Dans le Template, là on nous allons rendre le bouton rond, on comprend facilement que si je place la couleur du fond à bleu cette dernière sera figée. Je vais donc concevoir un bouton rond sans couleur (ou plutôt qui prépare le terrain à la réception d’une couleur quelconque, ce qui est assez différent en réalité).

Mais on m’a demandé un bouton rond bleu… Je place alors mon Template dans un style (si ce n’est pas déjà fait) et c’est là que je vais indiquer que le Background du bouton est bleu. Et c’est depuis les ressources de style qu’on puisera le bouton bleu, le Style amenant à la fois la forme (le Template) et l’aspect (bleu). Facile non ?

Oui mais… cela ne marche pas. J’ai beau choisir un Background bleu dans le Style, le bouton rond ne change pas d’aspect…

En effet, en créant un bouton rond, j’ai bien été obligé de faire le ménage de tout ce qu’il y avait dans le bouton d’origine. Notamment tous les rectangles qui définissent la forme de l’objet. Parmi eux se trouvaient certainement celui qui était “considéré comme” le fond. Sans lui, la propriété Background originale de la classe Button ne sait plus à qui attribuer la nouvelle couleur !

La cassure du templating

En templatant le bouton j’ai bien entendu été obligé de partir de zéro, rien ne pouvant être conservé (à part le ContentPresenter mais on ne le garde pas puisqu’on part d’un template vide en général pour ce genre de travail). Le problème serait le même si j’avais modifié un Template existant ou avais touché de quelque façon que ce soit le visuel d’un composant via templating. Il s’agit d’un cas général, et non pas d’une difficulté ponctuelle.

Seulement malgré la grande souplesse de Xaml il n’a pas de magie non plus… Le code original du bouton savait à quel objet de design il fallait changer la couleur quand la propriété Background était modifiée. Mais maintenant que j’ai supprimé tous les rectangles originaux et que j’ai créé un visuel original utilisant d’autres briques (des ellipses, des images png, des courbes de Béziers, etc) comment diable la propriété Background pourrait-elle agir sur mon bouton ? Quelle partie graphique (ou quelles parties graphiques) est (sont) impliquée(s) quand la propriété Background est changée ?

C’est ce que j’appelle la “cassure du template”.

Le template est une blessure, une distorsion entre un code original compilé pour un visuel donné et la souplesse de Xaml qui permet de modifier du tout au tout ce visuel…

En effet, la classe Button expose toujours une propriété Background, cela est géré par une propriété de dépendance dans le code C# de la classe Button. Un code compilé et immuable. Les changements de couleur de fond étaient correctement gérées par ce code qui savait quel rectangle jouait le rôle de fond.

Mais désormais le code de la classe Button se trouve dans l’impossibilité d’effectuer cet appareillage et aucun mécanisme automatique ne peut être conçu pour le faire. Graphiquement tout est possible, graphiquement n’importe quoi peut être utilisé pour faire “le fond”, notion qui elle-même n’a pas de définition très nette (on parle du fond du rectangle définissant le UserControl, le fond d’un élément de ce UserControl, de plusieurs éléments ?)

Il faut donc réparer ce qu’on a cassé !

Docteur Template Binding

Il nous faut un remède, cela est sûr. Un Template qui laisse des propriétés de l’objet original “en l’air” avec tous les “fils débranchés” n’est pas un Template fini.

C’est là qu’intervient ce bon docteur “Template Binding”. Et oui, cela sert à ça le Template Binding : à réparer les cassure du templating ! On peut lui trouver d’autres usages mais celui-ci est en tout cas le plus fréquent.

Je reprends ainsi le Template de mon joli bouton rond et je sélectionne l’ellipse que _je_ considère comme étant le “fond” de mon objet. Je vais sur sa propriété Fill et là je créé un Template Binding avec la propriété Background du bouton.

Je viens de réparer la cassure du templating…

Et ce qui est vrai pour la propriété Background du bouton l’est aussi pour toutes les autres propriétés que mon nouveau design aura ainsi “débranchées” du code original. Et, bien entendu, le bouton n’est qu’un exemple, cela est valable pour tout contrôle dont on modifie le Template.

Retour au Style

C’est bien joli ces histoire de cassures et de template binding, mais maintenant j’ai un bouton avec un fond noir (ou autre cela dépend) quand je suis en mode templating. Et quand j’applique mon template à un nouveau bouton c’est pareil ! Au secours !

Pas de panique… Fixer la couleur du Background de notre bouton c’est justement le job du Style, pas du Template. Il faut donc modifier le style entourant le Template ou bien créer un Style qui intègre le Template et passer en modification du Style lui-même.

Là, je vois toutes les propriétés de la classe bouton et je peux choisir le bleu qui a été défini pour les boutons de l’interface de l’application.

La cassure est définitivement réparée cette fois ci grâce à un niveau hiérarchique supérieur : le Style qui se place au-dessus de notre Template.

Un Template qui a du Style !

Désormais, pour poser un bouton rond et bleu sur une page de notre application il suffira soit d’appliquer le Style à tout bouton existant, soit, plus simplement, sous Expression Blend, de faire un drag’drop du Style sur la surface de travail, ce qui créera une instance de bouton avec le Style correspondant en une seule opération.

Bien entendu, si mon Template à du style c’est parce qu’un Style peut contenir un Template !

Conclusion

Un Template gère la forme, le visuel “profond” de l’objet, tout ce qui ne peut se changer par une simple propriété.

Le Style gère des couples propriété / valeur et permet de fixer l’aspect final d’un contrôle. Le plus souvent ses couleurs, le nom de sa fonte, la taille de celle-ci, etc. Il faut voir cela comme une “feuille de style”.

Mais un Style peut très bien “encapsuler” un Template. D’abord parce que Template est une propriété comme une autre et qu’un Style peut donc affecter une valeur à cette dernière. Ensuite parce qu’il est généralement judicieux d’insérer un Template dans un Style pour justement réparer les “cassures” du Templating.

Cela devrait être plus clair maintenant non ? Si tel n’est pas le cas, les commentaires sont ouverts pour cela  !

Stay Tuned !

Faites des heureux, PARTAGEZ l'article !