Dot.Blog

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

Responsive Design sous UWP

Créer des designs réactifs qui fonctionnent avec tous les form factors est un challenge. UWP met à notre disposition de nombreuses outils pour nous faciliter les choses. Entre autres le RelativePanel et les VisualState Triggers…

Responsive ou Reactive Design

Peu importe le nom qu’on lui donne il s’agit toujours de la même chose : concevoir des Design réactifs. Cela n’enlève rien à toutes les choses qu’il fallait prendre en compte pour créer un bon design auparavant, cela ne fait qu’ajouter une contrainte de plus !

On pourra chipoter des heures la nuance entre responsive (sensible, “qui réagit bien”) et reactive (réactif) qui semblent bien des synonymes une fois traduits… Entre bien réagir et être réactif le philosophe verra mille nuances, en ce qui concerne une poignée de contrôles visuels qui n’ont ni âme ni volonté propre cela simplifie malgré tout grandement le champ des interprétations possibles… Toutefois il ne faut pas confondre tout cela avec l’Adaptive Design me direz-vous ! Certes, mais une UI “adaptative” n’est rien d’autre qu’une UI “qui réagit bien” et qui est “réactive” vous répondrai-je alors !

Cette contrainte est de taille (!) car des form factors différents il en pleut tous les jours …

Bien entendu Microsoft est le SEUL éditeur d’OS au monde à posséder déjà l’outil magique pour gérer une telle situation : un langage d’UI Vectoriel, XAML.

Qui mieux qu’un outil vectoriel peut permettre de s’adapter à toutes les tailles, toutes les résolutions ? Aucun. Ce ne sont pas les minables nine-patches ou autres combines qui nous renvoient 30 ans en arrière à la naissance du Web et des premiers sites avec collage d’images découpées en petits morceaux qui vont permettre d’écrire un démenti…

Et XAML étant vivant et bien vivant, il s’adapte aux nouvelles contraintes, facilement, logiquement, naturellement.

Par exemple dans le cadre du Responsive Design il peut s’avérer utile (et nous le verrons plus bas) de positionner les éléments de façon relative les uns par rapport aux autres. La notion de Panel existe déjà sous XAML, avec déjà des panels spécialisés (StackPanel, WrapPanel,…) ajouter le RelativePanel ne demande aucun bouleversement.

Pour aller plus loin il peut s’avérer encore plus utile de faire basculer l’affichage dans des états différents selon, par exemple, la résolution de la machine (donc au runtime). XAML possède déjà la notion de Trigger, de VisualState. Ajouter un trigger qui réagit à la résolution n’est qu’un jeu d’enfant. Et le VisualState.Trigger s’ajoute là aussi naturellement pour faire que XAML reste ce qu’il est depuis la première heure : le fleuron de l’intelligence en matière de création d’UI.

Ce blog n’est qu’un long cri d’amour à C# et XAML, près de 800 articles en 7 ans dont certainement 80 à 90 % sont consacrés directement ou non à ces deux langages. UWP me donne à nouveau l’occasion de montrer leur modernité et je ne vous cacherai pas ma joie Sourire !

RelativePanel

C’est un Panel XAML donc une zone pouvant contenir des contrôles visuels. Ce qui différencie tous les Panels XAML les uns des autres c’est la stratégie de placement des contrôles enfants.

Le canvas qui est un panel aussi autorise un positionnement absolu, au pixel près. On ne l’utilise vraiment plus jamais car sa “stratégie” de placement est le niveau zéro de la stratégie tout court… Déjà à ses débuts le canvas n’était pas très recommandé en raison de son manque de souplesse. Le StackPanel est déjà plus évolué et constitue un élément de base de toute mise en page XAML, il sait placer automatiquement tous ses enfants les uns derrière les autres, horizontalement ou verticalement. Le WrapPanel introduit plus tardivement notamment dans WPF met en page ses enfants séquentiellement de gauche à droite en sachant couper les lignes pour en créer de nouvelles selon les contraintes de largeur qui lui sont imposées.

Il n’y a pas de limite à la sophistication d’un Panel XAML.

Le RelativePanel fait partie de ces panels montrant une certaine intelligence active. Il n’a pas de stratégie de placement propre comme le StackPanel ou le WrapPanel mais il offre des outils pour que le designer puisse développer un stratégie basée sur des positions relatifs des contrôles visuels les uns par rapport aux autres.

Comme presque tous les Panel XAML le RelativePanel offre à ses enfants des propriétés spéciales appelées propriétés attachées. Je renvoie le lecteur ignorant de ces subtilités de XAML à mes nombreux ouvrages et articles notamment le livre “XAML” dans la collection ALL.DOT.BLOG qui ne traite que de XAML et dans lequel vous trouverez des explications et des exemples de propriétés de dépendances et de propriétés attachées.

Le RelativePanel offre tout un … panel de propriétés attachées permettant ainsi à ses enfants de définir leur position par rapport aux autre enfants du panel. Ces éléments doivent être nommés et désormais pour simplifier les choses XAML donne un nom par défaut systématiquement à tout contrôle posé sur la surface de design. Ce ne fut pas toujours le cas mais l’existence même de RelativePanel rend ce changement de comportement par défaut tout à fait logique.

Le RelativePanel autorise ainsi une mise en page basée sur les liens sémantiques qui relient les éléments entre eux. Tel contrôle à vocation à être à gauche de tel autre qui lui-même n’a de sens qu’en étant au-dessus d’un troisième, etc.

En quoi le RelativePanel est-il une aide au Responsive Design ?

Le stackPanel est par essence dédié au Responsive Design étant capable de réorganiser ses enfants en fonctions de règles positionnelles. Dans le cadre d’un affichage fixe un tel comportement n’aurait guère d’intérêt. Bien entendu le RelativePanel est exploitable pour lui-même, uniquement parce que la stratégie de placement qu’il offre est originale et puissante. Mais on sent bien qu’il prend tout son sens dès lors  que l’affichage peut changer de taille au runtime.

Dans un Grid une fois les colonnes et les lignes définies la seule chose qui puisse s’adapter c’est la largeur ou la hauteur des cases ainsi définies. Mais l’organisation visuelle est toujours la même, elle ne peut pas s’adapter. Ce qui se trouve sur une même ligne ou même colonne le restera toujours.

Le RelativePanel est un peu comme cela il est vrai, une fois les relations posées entre les éléments elles sont ce qu’elles sont. Oui mais.. il est très difficile au runtime de modifier du tout au tout la mise en page d’une Grid (faire passer une cellule d’une ligne à une autre n’est pas possible, on peut changer le contenu mais cela demande du code et n’est pas tout à fait identique), alors qu’avec un RelativePanel il est très simple de modifier la propriété attachée d’un enfant pour lui dire ne soit plus à gauche de tel autre enfant mais positionne toi “au dessus”. Par exemple sur un écran large je peux vouloir mettre les libellés devant les boites de saisie alors que sur un écran plus petit le libellé sera placé au-dessus du champ à saisir.

Ce genre de changement d’organisation spatiale deviendrait un cauchemar avec une Grid, c’est un jeu d’enfant avec le RelativePanel…

Le Responsive Design implique cette gymnastique de placements pouvant changer en fonction de l’orientation (portrait/paysage) et de la résolution (en pixels effectifs sous UWP).

En WPF à l’aide de tous les panels existants il est tout à fait possible de créer des affichages pour PC qui savent tirer parti de l’espace supplémentaire qu’offre un 26” sur un 16”, mais là est bien toute l’adaptation à faire.

En UWP à ces mêmes contraintes s’ajoutent celles de la résolution et de la taille qui va que quelques pouces (2 ou 3) à SurfaceHub qui fait 84 pouces  en 4K HD !

Le ration même peut changer entre un écran de smartphone en portait et un écran de Hololens…

Il résulte de tout cela que bien que le concept de RelativePanel pourrait s’avérer très utile même sous WPF il prend tout son sens dans le cadre de UWP et du Reactive Design.

Réactif mais comment ?

Il est vrai que gérer la réactivité d’un RelativePanel est une autre affaire… il s’appelle d’ailleurs Relative panel et non Reactive panel. Son truc à lui c’est le positionnement relatif, pas la réactivité aux changements…

Alors comment faire ? Ecrire beaucoup de tests et de code pour modifier les propriétés des enfants d’un RelativePanel serait possible mais ne semble guère s’accorder avec les simplifications et la modernité de UWP…

Non, heureusement il y a mieux que ces horreurs à la iOS ou l’Android, beaucoup mieux. Il y a les VisualState.Triggers !

VisualState.Trigger

Là aussi pour rendre mon exposé par trop long je en peux détailler ce qu’est un VisualState en XAML à ceux qui ne le savent pas déjà. Et je leur conseillerai comme tout à l’heure de se référer à mes livres ou articles qui traitent de XAML.

Pour ceux qui savent déjà, les états visuels d’une page XAML sont d’une grande souplesse puisqu’on peut définir les états que l’on veut répartis dans des groupes différenciés, par exemple le groupe Work qui peut avoir les états RunningState et IdleState, permettant d’adapter la vue selon que l’application travaille ou est en attente (ce n’est qu’un pur exemple). Les états de chaque groupe pouvant changer indépendamment alors qu’à l’intérieur d’un groupe un seul état peut être actif à la fois.

On connait alors dans ce cas aussi le rôle des Triggers, des “déclencheurs” qui entrainent le changement de valeur d’une propriété selon la valeur d’une autre qu’ils surveillent… En général la propriété surveillée est directement ou non un évènement comme par exemple “MouseMove” ou “MouseOver”, ce qui permettra de faire passer une zone en rouge quand elle est survolée et cela sans code C#. On peut déclencher des animations et des comportements complexes mais le principe reste le même.

Sous UWP les Visual State Manager a connu quelques améliorations mais la plus marquante est l’ajout d’un trigger spécial l’AdaptiveTrigger. Ce dernier surveille de lui-même la largeur et la hauteur de l’écran. Et lorsqu’il réagit aux bornes données on peut utiliser des VisualStates.Setters pour modifier les propriétés de plusieurs contrôles.

On pourrait faire la même chose avec du code C# qui surveillerait via des events le changement de taille et qui basculerait les états visuels créés en XAML avec un GotoState(). Mais il faut avouer que le fait que l’AdaptiveTrigger le fasse tout seul en XAML sans invoquer le moindre code externe est un avantage important !

AdaptiveTrigger et RelativePanel : un couple parfait !

Même s’ils ne sont pas fait pour travailler exclusivement ensemble il ont malgré tout été pensé pour faire un couple d’enfer qui mériterait une couverture de Voici ou Gala !

En effet, une fois qu’on a compris l’avantage de chacune de ces nouveautés de XAML on commence a en entrevoir le potentiel lorsqu’elle sont couplées.

“Si l’écran est plus large que 750 pixels alors je mets le libellé A à gauche de la zone de saisie B. Si l’écran fait moins de 720 pixels mais plus de 400 je peux conserver ce placement en diminuant la taille du libellé qui restera lisible, mais en dessous, le libellé A doit passer au dessus de la zone de saisie B et en même temps devenir un peu plus petit…”

Cette phrase raconte une histoire, celle d’une UI vivante et réactive… Et tout cela se réalise en XAML en combinant l’AdaptiveTrigger (pour détecter les changements de taille) et le RelativePanel (pour changer facilement l’organisation spatiale des éléments) en plus des changements classiques de propriétés qu’un Trigger peut autoriser (la taille de la fonte dans cet exemple).

Savoir s’adapter

Comment déterminer quels sont les tailles à prendre en compte ? Microsoft définit dans UWP des familles de machine, ce qui peut donner un premier indice. Une machine de la famille Mobile sera forcément de plus petite taille qu’une machine de la catégorie Desktop. Et comme UWP raisonne en pixels effectifs et non en pixels réels, pas de prise de tête à avoir quand on prend en compte les résolutions… Car certains smartphone en 400 DPI par exemple peuvent avoir une taille supérieure en pixels réels à un écran de 19” en 72 DPI… Les autres OS laissent ce genre de considération de côté, à vous de vous enquiquiner…

Avec UWP tout est fait pour vous rendre la vie facile. Les tailles étant en pixels effectifs on peut facilement raisonner sur une échelle linéaire qui laisse de côté la résolution. Microsoft propose par exemple cette segmentation :

image

De même qu’ils indiquent avec précision les comportements qu’on peut attendre en fonction de ces tailles ce qui guide le design de façon appréciable :

image

 

Ainsi, à l’aide ces informations il est assez facile de savoir comment programmer les AdaptiveTriggers pour qu’ils réagissent convenablement dans toutes les circonstances.

Créer des UI réactives pour tous les form factors avec un seul code n’a jamais été aussi facile. Même les Xamarin.Forms que j’adore ne vont pas aussi loin limitées qu’elles sont par les deux OS préhistoriques sur lesquels elles fonctionnent…

une démo ?

J’ai repris une démo Microsoft que j’ai un peu adaptée mais qui reste dans l’esprit de ce qu’elle montrait, le RelativePanel avec les AdaptiveTriggers… Tout juste ce dont nous avions besoin ici !

Voici comment elle apparait en mode local machine, donc desktop :

image

 

Sur le même bureau, si je diminue la largeur, à un moment donné nous obtenons :

image

 

Comme on le voit il faut scroller pour tout voir et certains groupes de contrôles sont passés de la droite des images à sous les images. Mais le menu est encore ouvert car la place le justifie (c’est un choix du Designer qui est supposé connaitre les utilisateurs et leurs besoins… revoir mes articles sur le design et ma vidéo Youtube sur la question…).

Mais si j’exécute le même code sur un smartphone, disons en 5” :

image

 

Le menu s’est rétracté, il n’est visible qu’en touchant du doigt les 3 barres en haut à gauche :

image

Tout cela est magique car il n’y a aucun code pour le gérer en dehors du XAML de la Vue qui a elle seule peut s’adapter d’un petit smartphone à une phablette, une tablette ou un écran de PC de bureau…

Plutôt que de vous lister tout le code je préfère vous mettre en fin d’article le source de cette démo (il faut VS 2015 final et le SDK Windows 10) cela vous permettra d’étudier les subtilité XAML dans un environnement étudié pour… (je vous conseille d’utiliser Blend qui permet de voir les états visuels, essentiels ici). Attention pour des raisons de taille j’ai supprimé dans le réperoire Assets les images utilisées, vous pouvez prendre n’importe lesquelles et les nommer de la même façon ou modifier le code XAML pour afficher les vôtres.

Conclusion

UWP est une plateforme extraordinaire, elle est dotée aussi d’outils d’une rare intelligence comme Blend ou VS, de langages modernes et d’un système de description visuel vectoriel qui fait passer tout le reste pour des antiquités.

Vous aimez vous aussi C# et XAML ? Vous allez adorez UWP ! (et votre patron aussi le devrait si vous lui expliquez bien, car UWP ) rationalisation des développements, capitalisation du savoir et des équipes en place, accès à tous les form factors…).

Bon Dev,

Stay Tuned !

 

cs.zip (199,9KB)
blog comments powered by Disqus