Dot.Blog

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

Lorem Ipsum generator : un autre exemple Silverlight

Et un de plus dans la galerie ! :-)

https://www.e-naxos.com/slsamples/lorem/loremgen.html

“Lorem ipsum…” vous connaissez tous ce texte en pseudo-latin que tous les infographistes utilisent pour tester la mise en page de leurs documents, de la carte de visite aux sites web, de la plaquette commerciale sur papier glacé jusqu’à, bien entendu, des applications Silverlight.

Il existe beaucoup d’extraits de ce fameux texte. Je vous propose “ma” version, en Silverlight.

image

Fonctionnalités

Génération

LIG (Lorem Ipsum Generator) est capable de générer des Paragraphes, des Phrases et des Mots de façon aléatoire.

Une phrase est constituée de mots, il est possible de choisir une fourchette, par exemple entre 3 et 8 mots. Le tirage est aléatoire pour chaque phrase.

Un paragraphe est constitué de phrases, il est ici aussi possible de choisir une fourchette, par entre un paragraphe fait de 5 à 8 phrases.

Qu’il s’agisse de paragraphes, de phrases ou de mots, la “chose” générée le sera en plusieurs exemplaires (tous différents) et ce nombre est fixé par une fourchette.

Les radio boutons en bas à gauche permettent de choisir le mode de fonctionnement.

Une case à cocher sous le texte permet de forcer la première phrase (en mode Paragraphes) à sa valeur la plus connue, le célèbre “Lorem ipsum …”.

Visualisation et statistiques

Le texte généré est immédiatement visualisé à l’écran. De plus, LIG fournit des statistiques simples mais importantes sur ce dernier : nombre de symboles et nombre de mots.

Récupération du texte

Le plus simple est de cliquer sur le bouton “Copy”. Une confirmation est demandée par mesure de sécurité. Il suffit ensuite de coller ce texte où vous en avez besoin…

Bouton Info

Il affiche une page d’information sur l’application

Installation Out-Of-Browser (OOB)

LIG supporte le mode de fonctionne OOB, en cliquant sur le bouton “Install” (en haut à droite, il s’illumine quand on passe la souris dessus) l’application LIG sera téléchargée depuis le Web et installée sur votre machine. Selon votre choix une icône peut être placée sur le bureau ainsi qu’une entrée dans le menu Démarrer (ou l’équivalent sous Mac).

Lors de la première installation l’application sera ouverte immédiatement, le site web sera toujours à l’écran aussi. Fermez ce dernier. Vous disposez maintenant d’une application locale totalement indépendante… Bien entendu LIG n’a alors plus besoin de connexion Internet pour fonctionner et durant son utilisation ni il n’envoie ni il ne reçoit des données depuis Internet. C’est totalement safe et sans espionnage.

Conclusion

Du coup, me privant d’une remontée d’info automatique pour respecter la liberté de chacun de ne pas être pisté par la moindre application, je remercie par avance les utilisateurs de LIG de bien vouloir me laisser un mot pour me dire s’ils apprécient ou non l’application.

Le code source sera mis à disposition d’ici quelques temps, alors… Stay Tuned !

Article: M-V-VM avec Silverlight

Model-View-ViewModel, je vous en parlais il y a très peu de temps (MVVM, Unity, Prism, Inversion of Control…) et je vous avais promis un exemple pour rendre tout cela plus concret. C’est fait ! Et même mieux, un article de 70 pages l’accompagne !

Vous saurez tout (ou presque) sur cette design pattern absolument incontournable pour développer sérieusement sous Silverlight.

Après des explications sur la pattern elle-même l’article vous présente une application exemple entièrement réalisée avec ce qu’il y a “out of the box”. J’ai fait le choix de n’utiliser aucun Framework existant (Prism, Cinch, Silverlight.FX, MVVM Light…) pour vous montrer que M-V-VM peut entièrement être mis en œuvre “à la main” sans aide extérieure.

Cela ne veut pas dire que tous ces Frameworks (dont l’article parle aussi) ne sont pas intéressants, au contraire ! Mais comment choisir une librairie facilitant M-V-VM si vous ne savez pas comment mettre en œuvre cette pattern et si vous ne connaissez pas les difficultés qu’elle soulève autant que ses avantages ?

Cet article vous permettra de faire le point sur M-V-VM et de pouvoir ensuite choisir le Framework qu’il vous plaira en toute connaissance de cause ou bien cela vous aidera à développer votre propre solution. Après tout, l’application exemple fonctionne parfaitement sans aucun de ces Frameworks….

Le code source du projet est fourni. En raison de l’énorme avantage de la gestion des commandes introduites dans Silverlight 4 (toujours en beta) l’article utilise cette version qui sera bientôt disponible. Tout est expliqué pour savoir comment faire fonctionner le code exemple à l’aide de VS 2010 ou Blend 4 (en beta aussi).

L’article peut être lu sans faire tourner le code si vous ne souhaitez pas installer la beta de SL4, et la première partie théorique s’applique aussi bien à M-V-VM sous SL3.

Bonne lecture !

(PS: n'oubliez pas que depuiis août 2012 les articles sont regroupés sur la page publications).

Téléchargement ici : M-V-VM avec Silverlight, de la théorie à la pratique.

 

 

 

 

Happy New year et MVP 2010 !

Je vous souhaite à tous une excellente année 2010, une année qui commence fort puisque je viens d'apprendre ma nomination MVP 2010 "Client App Dev" pour mes contributions principalement axées sur le développement des applications clientes sous WPF et Silverlight.

Cette récompense fait suite à ma nomination l'année dernière au titre de MVP C#. Cette complémentarité de compétence Framework-Langage / IHM traduit la réalité de mon engagement sur .NET depuis plusieurs années que vous pouvez constater ici même sur Dot.Blog par le nombre des articles et billets que je publie (108 pour être exact pour l'année passée !) sans compter tous les exemples de code et les applications exemples Silverlight vivantes intégrées aux billets.

Je suis heureux, et honoré, de partager cette nouvelle distinction MVP avec tous les lecteurs de Dot.Blog,

Que cette année vous apporte à vous et vos proches tout ce que vous en attendez !

Happy New Year 2010 !

 

Ma lettre de nomination MVP : ERL MVP 2010 OD.pdf (172,84 kb)

Happy Holidays !

Patientez quelques instants que le téléchargement de la petite application Silverlight ci-dessous se termine...

Ensuite passez la souris sur ma tête pour lire le message !

[silverlight:source=/SLSamples/JFetes2009/IlNeige.xap;width=480;height=320]

un peu de technique

Il faut bien que cela soit l'excuse de parler technique, au moins un peu...

Donc cette petite carte de voeux a été créée en utilisant l'exemple "Il neige" largement commenté ici (avec le source, voir "« Il neige » ou les boucles d’animation, les fps et les sprites sous Silverlight"). La transformation majeure est bien entendu de faire tomber des cadeaux et ma trombine de temps en temps à la place de certains flocons de neige.

Pour aller vite j'avoue que j'étais parti pour faire deriver les nouveaux sprites de la classe "Flocon" existante dont les instances sont multipliées pour créer l'effet de neige dans cette petite gestion de particules.

Pas d'héritage de UserControl

Trois fois hélas, créer un UserControl en héritant d'un autre n'est pas impossible sous Silverlight 3 mais c'est un peu sportif... Pour éviter d'entrer dans les manipulations nombreuses et hasardeuses permettant de contourner ce problème (temporaire on l'espère) j'ai utilisé la solution des interfaces.

J'ai donc créé une interface ISprite qui reprend les deux choses importantes pour la boucle d'animation : l'objet est-il passé hors zone (il doit alors être détruit) et l'action "Tombe" qui est appelée à chaque pas d'animation.

Le flocon original a été modifié pour supporter l'interface (ce qu'il faisant déjà avant) et la boucle d'animation (utilisant l'event de rendering de SL) a été modifiée pour utiliser des ISprite au lieu des Flocon originaux.

Ne restait plus qu'à créer autant de UserControl que de types de cadeaux qui peuvent s'afficher (un rouge, un vert, un bleu, plus ma bobine affublée d'une barbe rapidement dessinée par dessus). Chaque nouveau UserControl supporte ainsi ISprite. L'application fait des tirages au sort pour attribuer 25% de chances d'apparaitre à chacun des 4 sprites.

Améliorations diverses

Le reste est conforme à l'application "Il neige", à la création de chaque sprite celui-ci détermine une rotation et une taille aléatoire. Pour ma tête j'ai modifié la séquence pour que l'angle de rotation soit compris entre -45 et +45 degrés, de telle façon à ce que je ne me retrouve pas tête en bas ! Ce sprite a en outre été complété d'un phylactère annonçant "Joyeuses Fêtes" dont l'apparition et la disparition sont gérés via deux behaviors de type "GotoState". Lorsque la souris entre ou sort du contrôle ces derniers changent l'état du sprite et le message devient visible ou se cache avec une transition automatique gérée par le Visual State Manager.

Comme vous l'avez constaté lorsque la souris entre dans l'application une guirlande clignotante apparaît. Il s'agit d'une image vectorielle Illustrator passée (par simple copier / coller) vers Expression Design puis ensuite passée de la même façon sous Expression Blend à l'intérieur d'un nouveau UserControl. L'effet de chenillard est une animation réalisée "à la main" en mode "forever" en jouant sur l'opacité des bulbes et de leur reflet. Le contrôle est caché pour la souris ce qui fait qu'il est possible de cliquer sur ma tête "à travers" la guirlande bien qu'elle soit en premier plan.

Image de fond

Le logo E-naxos en 3D a été réalisé sous Swift 3D Version 6. Il a été transformé en Jpeg, car même si SL3 gérait la 3D la lourdeur d'une telle gestion ne s'imposait vraiment pas ici. C'est un fond statique et une image fixe est parfaite pour cela. En revanche un outil de 3D est nécessaire pour créer l'image !

Conclusion

Une façon ludique mais instructive de vous souhaiter à tous d'excellentes fêtes de fin d'années ...

Et en plus voici le code complet !

Amusez-vous bien et Stay Tuned !

Le code complet : IlNeige2010.rar (287,17 kb)

[EDIT] Voici le projet compressé en mode Zip, plus classique que le RAR que j'avais mis dans l'article : IlNeige2010.zip (290,57 kb)

MVVM, Unity, Prism, Inversion of Control…

Dans la série, presque culte, que je diffuse régulièrement et dont le titre serait “comment ne pas passer pour un idiot à la machine à café” la belle brochette de termes que je vous propose aujourd’hui a de quoi plonger le non initié dans une profonde perplexité !

Tous ces termes sont plus ou moins liés entre eux, les uns renvoyant aux autres ou les autres utilisant les uns.

MVVM ou plutôt M-V-VM

“Ah oui ! avec les tirets on comprend tout de suite mieux !” (Jean Sérien, lecteur assidu).

Model-View-ViewModel. Ca bégaye un peu mais c’est pourtant ça.

En réalité vous connaissez certainement le principe qui vient de M-V-C, Model-View-Controler, Modèle-Vue-Contrôleur. Une stratégie de développement visant à découpler le plus possible les données de leurs représentations. Sinon je vous laisse vous faire une idée, depuis dix ans au moins le Web est plein de pages présentant cette approche. Le billet du jour n’est pas d’aller dans les détails, juste de planter le décor avant d’aller plus loin dans d’autres billets.

Alors pourquoi MVVM plutôt que MVC ? C’est en tout cas le premier terme qui devient de plus en plus à la mode dans le monde WPF / Silverlight. Une réponse un peu courte je l’avoue… Mais même Prism (j’en parle plus loin) dans sa version 2 et qui, entre autres choses, présente cette stratégie ne l’évoque pas sous ce nom mais sous diverses autres formes. Autant dire que terminologiquement parlant, savoir ce que veut dire MVVM est un must de l’instant !

Donc M-V-VM est un modèle de développement, une façon de structurer l’écriture du code dont le but ultime est de séparer le Modèle (les données) des Vues (l’IHM ou l’UI), le tout en passant par un intermédiaire, le contrôleur qui s’appelle dans cette variante ViewModel (et pour lequel je ne trouve pas de traduction vraiment limpide).

La vraie question, légitime est : ce changement d’appellation cache-t-il un changement de rôle ? Les puristes vous diront bien entendu que oui, sinon ils n’auraient pas inventé un nouveau nom… Les pragmatiques dont je suis vous diront que lorsqu’on veut relancer un produit un peu trop connu mais toujours valable, on change son nom ce qui permet de communiquer à nouveau dessus plus facilement… Les méthodologistes sont des futés… Certains vont jusqu’à parler de parenté avec la pattern Presentation-Model, un peu comme d’autres vous expliquent que C# vient de C++ plutôt que de dire qu’il vient de Delphi. S’inventer des origines plus ou moins nobles (ou du moins qui le paraissent) est un jeu finalement très humain qui nous intéresse que peu ici.

Mais pour être totalement juste j’ajouterais qu’il y a en fait une véritable évolution de point de vue entre M-V-C, dont on parlait déjà du temps de Delphi Win32 et C++, et M-V-VM qu’on applique à WPF ou Silverlight. Par force le temps passe, les outils se sophistiquent et les pensées s’aiguisent. Donc, bien sûr, il y a quelques variations entre ce qu’on entendait par “contrôleur” dans MVC et ce qu’on entend par ViewModel dans MVVM. Mais, à mon sens, cela est mineur. D’ailleurs de nombreux forums proposent des threads à rallonge où des tas d’experts (autoproclamés) se déchirent sur ces nuances. Dépassons ces débats stériles.

Pourquoi MVVM est-il en train de s’imposer sous WPF / Silverlight ? Simplement parce que “out-of-the-box” Visual Studio et Blend ne permettent pas directement de vraiment séparer le travail entre les développeurs et les designers. Volonté pourtant clairement affichée de ces produits. Les outils eux-mêmes mélangent un peu des deux compétences (un peu plus de design dans Blend, un peu plus de développement dans VS).

Mais lorsqu’il faut écrire de vraies applications avec WPF ou Silverlight et qu’on a de vraies équipes à gérer, c’est à dire des informaticiens d’un côté et un ou plusieurs infographistes de l’autre, il faut bien convenir que sans une approche appropriée ça coince. Non pas que techniquement les outils (Blend, VS) ne sont pas à la hauteur, ni même que les langages (Xaml, C#, VB) ne sont pas bien étudiés. Au contraire. La faille est ici humaine et méthodologique.

En réalité tous les outils du monde, aussi bien faits soient-ils, ne peuvent permettre aux informaticiens l’économie d’une méthodologie bien pensée… Ce que Microsoft propose avec WPF / Silverlight, Xaml et le Framework .NET est très nouveau et il faut du temps pour maîtriser ces outils qui évoluent beaucoup en peu de temps. Dégager des stratégies de développement pertinentes est plus long que de créer un nouveau compilateur.

Or, si on regarde une fenêtre WPF ou une page Silverlight, on voit immédiatement qu’il existe du code-behind joint au fichier Xaml selon un modèle vieux de 15 ans (le couple de fichiers DFM/PAS des TForm de Delphi 1 en 1995 par exemple). La tentation est alors bien grande de coder l’action d’un click sur un bouton directement dans ce code puisqu’il est là pour ça ! Si c’est autorisé, c’est utilisé. Et malgré une séparation forte du code métier (ce qui suppose d’avoir fait cet effort) on se retrouve au final avec une interface qui contient du code qui en appelle d’autres (le code métier ou les données) voire pire, qui contient des petits bouts de traitement ! Autant dire que certains codes Silverlight ou WPF que j’ai audités récemment ressemblent comme deux gouttes d’eau au code spaghetti Delphi que j’ai audité dans les 15 dernières années… La vie n’est qu’un éternellement recommencement disent certains. Je n’ai pas cette vision nihiliste de l’existence, mais en informatique, hélas, cela se confirme !

Vade Retro, Satana !

En dehors de l’imbroglio qui résulte de cette situation, le code final de l’application est donc difficilement maintenable, intestable de façon cloisonnée (donc obligeant à des tests depuis l’interface par des testeurs ou des outils simulant un humain qui clique sur les boutons) et, comble du comble, il est en réalité presque impossible de séparer proprement le travail des informaticiens et des designers ! L’enfer brûlant s’ouvre alors sous les pieds des infortunés développeurs qui croyaient pourtant avoir “tout fait comme il faut”. Bad luck. On est loin du compte.

M-V-VM permet ainsi de régler plusieurs problèmes d’un coup : meilleure maintenabilité du code, vraie séparation entre code et interface, tests unitaires du code possibles sans même qu’il existe encore d’interface (ou totalement indépendamment de celle-ci), et enfin vraie séparation entre infographistes et informaticiens.

En pratique le fichier de code-behind ne sert plus à rien et reste vide. Les Vues (interface utilisateur) utilisent leur DataContext pour se lier à une instance du ViewModel qui lui sait utiliser le Model (les données). Grâce à cette notion de DataContext et au puissant Data Binding de Xaml on peut mettre en place une séparation bien nette avec, au final, peu de code en plus.

Reste le problème des événements de l’UI comme le simple clic sur un bouton. Comment le programmer sans code-behind ? Sous WPF la réponse est simple puisqu’il existe une gestion des commandes complète (le CommandManager et d’autres classes). L’interface ICommand est la base de ce mécanisme, les contrôles qui ont des propriétés de ce type peuvent donc être bindées au code des actions se trouvant alors placé dans le ViewModel, comme n’importe quelle autre propriété peut être liée via le binding.

Ce modèle est d’une grande élégance il faut l’avouer. J’aime plaisanter et je joke souvent dans mes billets, mais il ne faudrait pas croire que je passerais du temps à écrire un long billet sur un sujet futile ou une simple réinvention stérile de vieux procédés. M-V-VM est “la” façon de coder proprement sous WPF et Silverlight. Il n’y en a pas d’autres (d’aussi abouties pour l’instant en tout cas). Tout le reste ne peut que créer du code spaghetti. Cela peut choquer, vous paraître extrême dit comme ça, mais pourtant c’est la vérité…

C’est pourquoi je vous proposerais bientôt un autre billet abordant par l’exemple la stratégie M-V-VM.

Mais pour l’instant revenons deux secondes sur cette fameuse gestion des commandes de WPF. Et Silverlight ? Hélas malgré l’enthousiasme que vous me connaissez pour ce dernier et sa convergence avec WPF, il faut reconnaître que cette dernière n’est pas terminée. On le dit souvent, Silverlight est un sous ensemble de WPF, même si ce n’est plus tout à fait vrai. On avance aussi que le plugin est trop petit pour contenir tout ce qui fait WPF. C’est vrai. Mais comme je le disais dans un autre billet, avec une installation personnalisée du Framework ce dernier peut tenir dans 30 Mo. C’est bien plus gros que le plugin SL actuel. Mais j’ai joué pas plus tard qu’hier avec une application écrite en Adobe Air, et justement le download de Air fait environ 30 Mo. Pour un résultat tellement navrant que j’ai tout désintallé d’ailleurs. Alors d’ici quelques versions de SL, peut-être Microsoft (et les utilisateurs) accepteront-ils l’idée qu’un produit aussi bien ficelé puisse justifier les 10 ou 20 Mo de plus à télécharger (une fois) pour avoir enfin un équivalent absolu de WPF… Mais ce n’est pas encore le cas, force est de le constater.

Donc Silverlight pour le moment propose ICommand, l’interface, mais pas tout le reste qui fait la gestion des commandes sous WPF. De fait, dans Silverlight il faut ajouter un peu de code pour recréer cette dernière. C’est le but d’un petit framework comme “MVVM Light” qui se base sur les principes édictés dans Prism. Mais on peut inventer d’autres solutions.

Donc pour faire du M-V-VM avec WPF, tout est dans la boîte, avec Silverlight il faut ajouter une simulation, même minimaliste, de la gestion des commandes afin que ces dernières puissent être intégrées au ViewModel et que la Vue puisse être liée aux actions du ViewModel par un binding sur des propriétés de type ICommand.

Prism 2

Cela nous amène à évoquer Prism “patterns and practices : Composite WPF and Silverlight”.

Il s’agit d’une réflexion méthodologique dont les fruits se présentent sous la forme de conseils et d’applications exemples. Prism couvre de nombreux champs et propose des solutions élégantes.

Cela ne serait pas fairplay d’en parler en quelques lignes, c’est un travail d’une grande qualité qui ne se résume pas en trois phrases. Je vous conseille vivement sa lecture, rien ne peut remplacer cet investissement personnel.

Mais puisqu’aujourd’hui le but est de planter le décors, il aurait été tout aussi impardonnable de “zapper” Prism, surtout que certaines solutions permettant d’implémenter M-V-VM sous Silverlight y sont présentées.

Je reviendrai certainement un jour sur Prism, c’est d’une telle richesse que j’ai vraiment envie de vous en parler plus. Prism est incontournable, mais ne peut se résumer dans un billet. Si je trouve le moyen d’un tel tour de force alors je vous en reparlerai en détails. Mais le meilleur conseil c’est encore que vous le lisiez vous-mêmes…

Unity Application Block

Unity est un framework Microsoft qu’on trouve dans la série “patterns & practices”. Une mine d’or de la méthodologie sous .NET d’où provient aussi Prism dont je parlais ci-dessus.

Il faut noter que MS fait d’immenses efforts pour ouvrir la réflexion sur les méthodologies tout en tentant le tour de force de rendre ces cogitations tangibles. Lorsqu’on connaît un peu les méthodologistes pour les avoir fréquentés, toujours perdus dans leurs hautes sphères et rebutant à se “salir” les mains avec du code, on imagine à quel point l’effort de Microsoft est louable et tellement remarquable que cela mérite d’insister un peu sur la valeur de patterns & practices.

Ainsi, Unity, tout comme Prism, ne se contente pas d’être un gros pavé d’explications qui seraient réservées à une certaine élite. Unity se sont bien entendu des explications mais aussi du code qui permet d’ajouter l’action à la réflexion. Du bon code même. Des librairies utilisables pour simplifier le développement d’applications qui se conforment aux best practices. Je n’irais pas jusqu’à dire que les travaux présentés dans patterns & practices sont d’abord aisés, les cogitations méthodologiques réclament toujours un minimum d’attention, mais la volonté assumée de joindre la pratique et l’usage à la réflexion rend tout cela accessible à qui se donne un peu de temps pour lire et expérimenter.

Pour ce qui est de Unity, ce bloc se concentre sur la conception d’un conteneur “léger” et extensible permettant de faire de l’injection de dépendances (dependency injection).

De quoi s’agit-il ?

Comme indiqué en introduction de ce billet, tout ce dont je vous parle aujourd’hui tourne autour des mêmes besoins, de concepts proches ou qui se font écho les uns aux autres. L’injection de dépendances est une réponse à une série de problèmes qui se posent lorsqu’on désire séparer proprement des blocs de code tout en évitant de construire une “usine à gaz”.

Séparation de code, M-V-VM, Prism, ce sont autant de caméras placées autour d’une même scène. Des angles de vue différents, des manières d’entrer dans le sujet différentes, mais au final un but qui, s’il n’est pas commun, vise une même finalité. La conception d’applications souples, maintenables et extensibles.

Grâce à Unity vous disposez à la fois d’explications claires et d’une librairie permettant de gérer convenablement les injections de dépendances. Encore une terminologie “savante” pour des choses finalement pas si compliquées que cela. Mais pour en rajouter une couche disons que l’injection de dépendances n’est qu’une façon de gérer l’ “inversion de contrôle”.

Inversion of Control

“Pitié ! Arrêtez-le !” (Jean Peuplus, lecteur parisien)

On peut comprendre ce lecteur. Une telle avalanche ne risque-t-elle pas de tout emporter sur son passage, au risque de rendre ce billet contre-productif ?

La question mérite d’être posée, mais je fais le pari que vous tiendrez jusqu’au bout (qui est proche je vous rassure).

Le problème à résoudre

Pour info : j’utilise ci-après des parties de la documentation de Prism version octobre 2009

Vous avez une classe dont le fonctionnement dépend de services ou de composants dont les implémentations réelles sont spécifiées lors du design de l’application.

image

Les problèmes suivants se posent

  • Pour remplacer ou mettre à jour les dépendances vous devez faire des changements dans le code de la classe (classe A dans le schéma ci-dessus)
  • Les implémentations des services doivent exister et être disponibles à la compilation de la classe
  • La classe est difficile à tester de façon isolée car elle a des références “en dur” vers les services. Cela signifie que ces dépendances ne peuvent être remplacées par des mocks ou des stubs (maquette d’interface ou squelette non fonctionnel de code).
  • La classe contient du code répétitif pour créer, localiser et gérer ses dépendances.

Les conditions qui forcent à utiliser la pattern d’inversion de contrôle

  • Vous voulez découpler vos classes de leurs dépendances de telle façon à ce que ces dernières puissent être remplacées ou mises à jour avec le minimum de changement dans votre code, voire aucune modification.
  • Vous désirez construire vos classes de telle façon à ce qu’elles puissent dépendre d’implémentations inconnues ou indisponibles lors de la compilation.
  • Vous voulez pouvoir tester vos classes de façon isolée, sans faire usage des dépendances.
  • Vous voulez découpler la responsabilité de la localisation et de la gestion des dépendances de vos classes.

La solution

Il faut déléguer la fonction qui sélectionne le type de l’implémentation concrète des classes de services (les dépendances) à un composant ou une source externe à votre code.

Les implémentations

Il y a plusieurs façons d’implémenter la pattern d’inversion de contrôle. Prism en utilise deux qui sont l’Injection de dépendances (Dependency Injection) et le Localisateur de service (Service Locator).

image 

Injection de dépendances

L'injection de dépendances est un mécanisme qui permet d'implanter le principe de l'Inversion de contrôle. Il consiste à créer dynamiquement (injecter) les dépendances entre les différentes classes en s'appuyant généralement sur une description (fichier de configuration). Ainsi les dépendances entre composants logiciels ne sont plus exprimée dans le code de manière statique mais déterminées dynamiquement à l'exécution.

Le localisateur de services

Le localisateur de services est un mécanisme qui recense les dépendances (les services) et qui encapsule la logique permettant de les retrouver. Le localisateur de services n’instancie pas les services, il ne fait que les trouver, généralement à partir d’une clé (qui éviter les références aux classes réelles et permet ainsi de modifier les implémentations réelles). Le localisateur de services propose un procédé d’enregistrement qui liste les services disponibles ainsi qu’un service de recherche utilisé par les classes devant utiliser les dépendances et qui se chargent de les instancier.

Exemples

Même si les choses sont certainement plus claires maintenant (je l’espère en tout cas) il est certain que des exemples de code aideraient à visualiser tout cela. J’en ai bien conscience et j’ai prévu d’illustrer concrètement les principes expliqués dans ce billet. Toutefois en raison de la longueur de chaque exemple (nous parlons ici de techniques de découplement donc de nombreux fichiers et projets pour un seul exemple) il ne me semble pas raisonnable d’aller plus loin immédiatement. Laissons ce billet se terminer, et toutes ces notions tourner dans vos têtes avant de les appliquer !

Conclusion

Une terminologie ésotérique ne cache pas forcément des choses complexes. Le but de ce billet était de vous donner quelques clés pour mieux comprendre un ensemble de notions différentes mais participant toutes à une même finalité.

J’aborderai des exemples sous Silverlight très bientôt. Donc pour en savoir plus :

Stay Tuned !

Quelques conseils de design (UserControl, Blend, Visual State manager, Silverlight / WPF)

L’une des avancées les plus intéressantes introduite dans Silverlight 2 (puis reprise sous WPF et naturellement sous Silverlight 3) est très certainement le Visual State Manager. Gestionnaire des états visuels simplifiant la conception visuelle des contrôles (UserControl). Bien utiliser le VSM, outre de rendre plus simple la représentation des états visuels d’un composant, apporte aussi une clarification essentielle à la gestion des transitions entre ces derniers.

Etats et transitions

Un contrôle peut être ou non visuel. Un Timer n’est pas visuel. Une Combobox l’est. Nous parlerons bien entendu ici uniquement des contrôles qui possèdent un visuel.

Un Etat visuel peut être compris comme une allégorie d’un ou plusieurs états logiques du contrôle. Les états logiques sont ceux définis dans le code fonctionnel, comme par exemple IsEnabled ou IsChecked. Il ne faut pas confondre état et propriété. Les états sont le plus souvent représentés par des propriétés (ils peuvent simplement être des champs internes ou le résultat d’un calcul) mais toute propriété ne représente pas un état (la couleur Foreground ou le type de curseur en sont des exemples).

je parle d’allégorie car comme je l’évoque dans le billet “Le défit des nouvelles interfaces Silverlight et WPF – La cassure conceptuelle”, la représentation visuelle d’un contrôle (et de ses états) est l’aboutissement d’une démarche intellectuelle et conceptuelle qui a justement pour but de créer un univers dans lequel les acteurs (les contrôles) doivent chacun avoir leur place et leur comportement. Entre visuel et fonctionnel il n’y a pas de relation d’identité (au sens d’un schéma conceptuel de données - relation de type 1-1). Le visuel créé une identité pour l’acteur, ce qui est différent et ne s’entend pas dans le même sens.

Je disais aussi qu’un état visuel représente un ou plusieurs états logiques. Dans certains cas deux (ou plus) états logiques peuvent être “résumés” par un seul état visuel. Cette combinaison prend son sens ponctuellement, au cas par cas, et ne peut pas être généralisée en une règle absolue. Néanmoins, lorsqu’on créé le visuel d’un contrôle, il faut garder à l’esprit cette possibilité. L’œil peut discerner de nombreuses subtilités dans les formes et les couleurs alors qu’il lui faut plus de temps pour interpréter des séries de données chiffrées ou textuelles…

Les transitions ont aussi leur importance. On ne passe pas d’un état visuel à un autre de façon abrupte, sauf si cela est volontairement assumé. Les transitions fluidifient l’interface, créent une continuité visuelle. Si les transitions ne portent pas de sens en elle-mêmes, à la différence des états, elles jouent un rôle important dans l’expérience utilisateur (UX).

Trois phases

La conception des états visuels et des transitions peut se découper en trois phases différentes.

Phase statique

Cette phase de la conception consiste à créer des états dans le VSM et à définir l’aspect du contrôle dans chacun d’eux. On en profite pour identifier les groupes d’états.

Au sein de chaque groupe les états sont mutuellement exclusifs, les états de différents groupes étant indépendants et simultanés (une Checkbox peut à la fois avoir le focus et être cochée ou décochée par exemple. IsChecked appartient à une groupe d’états différent de celui définissant l’aspect visuel pour le focus).

Lors de la phase de conception statique on se préoccupe de l’aspect que prend le contrôle dans chacun des états. On vérifie aussi la compatibilité visuelle entre les états des différents groupes d’états. Si cette étape est qualifiée de statique c’est pour souligner qu’on ne se soucie pas encore des transitions (la dynamique visuelle), mieux vaut rester concentré sur les groupes d’états et les états eux-mêmes.

On veillera à ne pas manipuler une même propriété du contrôle dans plusieurs groupes d’états pour des raisons de cohérence. D’ailleurs si on commet cette imprudence Blend le signalera par un petit symbole de danger (triangle assorti d’un message de type tooltip). On peut assumer une telle situation si on en mesure toutes les conséquences, c’est pourquoi Blend signale le problème mais n’interdit pas la situation. Mais en règle générale cette alerte trahit une mauvaise conception !

Etat “Base”

Vous l’avez peut-être remarqué, dès qu’un groupe d’états existe le VSM ajoute systématiquement à la liste un état spécial appelé Base. Cet état permet de visualiser le contrôle hors de tous les états visuels définis. Toute modification effectuée dans l’état Base se propage à tous les états définis et n’est enregistré dans aucune time-line.

Etat par défaut

Un contrôle bien conçu devrait posséder pour chaque groupe d’états un état par défaut. En effet l’état Base n’existe pas en tant qu’état visuel, il s’agit juste d’un mode d’édition spécial du VSM. Lorsqu’une instance du contrôle est créée il est forcément dans un état donné (par exemple IsChecked=False pour une case à cocher). Il se trouve même souvent dans plusieurs états précis qui seront représentés par plusieurs groupes d’états. De fait il ne faut pas se reposer sur l’état Base mais plutôt créer un état par défaut pour chaque groupe qui traduit l’aspect visuel du contrôle lorsqu’il vient d’être initialisé (ou “remis à zéro” si une telle fonction est disponible dans le contrôle). Dans les contrôles existants vous pouvez remarquer que de tels états par défaut existent. Par exemple dans le groupe CommonStates de la classe Button on trouve un état Normal, dans le groupe CheckedStates (d’une case à cocher) on trouve UnChecked, etc.

L’état par défaut de chaque groupe ne doit pas forcément porter un même nom (ce qui poserait un problème pour le VSM de toute façon, il n’autorise pas que deux états portent le même nom). Au contraire, cet état par défaut de chaque groupe doit, comme dans les exemples donnés ci-dessus, porter un nom ayant un sens en rapport avec le groupe.

Il est parfaitement valide de créer un état par défaut (ou non) qui ne fait que recopier la situation de l’état Base. Il suffit de créer l’état et de ne rien modifier… Cela permet souvent de clarifier les choses. Prenons l’exemple de la Checkbox, la croix est ajoutée dans l’état Base mais est cachée. En revanche cela ne lève pas l’obligation de créer un état Unchecked dans le groupe CheckedStates. Cet état est juste créé mais non modifié puisqu’il reprend l’aspect de l’état Base (croix cachée).

Validation des états

Arrivé ici il faut tester tous les états. Cela peut se faire sous Blend mais il ne faut pas hésiter à créer une fiche de test, y placer le contrôle, et ajouter des boutons, sliders, et autres éléments d’interface pour tester au runtime le changement de chaque état, la cohérence entre les groupes, etc.

Dans certains cas très simples la conception des états visuels peut s’arrêter là. On peut vouloir des transitions franches et immédiates et il n’y a alors plus rien à ajouter.

Dans d’autres cas il s’avère essentiel d’aborder la seconde phase.

Phase des transitions

La version simple consiste à utiliser le réglage par défaut que le VSM propose pour chaque groupe d’états. Dans de nombreux cas cela peut être suffisant. Il suffit alors de définir un temps pour l’ensemble des transitions. Le VSM calculera à l’exécution les animations correspondantes.

La version plus évoluée consiste à utiliser les options du VSM pour chaque état afin de régler les transitions d’entrées et de sorties de celui-ci. Il peut en effet s’avérer nécessaire d’avoir des timings différents selon le sens de la transition et l’état précédent. Par exemple il est courant que les états visuels de type clic souris soient instantanés pour ne pas donner l’impression à l’utilisateur que le logiciel est “mou”, lent à répondre, alors que le relâchement de la souris peut au contraire accepter un temps assez long, donnant une sensation de douceur, d’amorti “luxueux”.

Encore une fois les choses peuvent s’arrêter là. Bien entendu après avoir testé toutes les transitions. N’oubliez pas que Blend 3 propose désormais une option permettant de visualiser en conception l’effet des transitions lorsque le VSM est actif. Cela fonctionne très bien, sauf pour les transitions utilisant des storyboards.

Phase des transitions dynamiques

Justement le troisième niveaux de personnalisation consiste à créer des storyboards au lieu de se contenter des timings réglés dans le VSM. Dans ce cas on créé une animation complète pour la transition en utilisant les fonctions des storyboards (time line, boucle for-ever, autoreverse…).

Pouvant s’utiliser dans les storyboards ou sur les animations par défaut créées par le VSM, les fonctions de ease in et ease out permettent d’ajouter une touche “organique”, plus naturelle, aux animations. Silverlight 3 propose de nombreux modes (comme le rebondissement par exemple) qui, bien utilisés, finissent le visuel et le rende plus “pro”.

Relation avec le code

Il y a toujours eu deux aspects à la création d’un contrôle personnalisé, même sous Win32 avec les MFC ou sous Delphi avec la VCL. La création du visuel d’un côté et celle du code fonctionnel de l’autre. Tout a changé mais pas cette séparation qui s’est, au contraire, renforcée.

Le designer (l’infographiste) conçoit le visuel d’un contrôle, il prévoit comment le contrôle se comportera pour l’utilisateur, comment il s’animera, comment ses différents états seront visualisés.

Pour l’informaticien le point de vue est différent. Il ne doit pas se soucier de l’aspect visuel mais du comportement du contrôle, de sa logique sous-jacente.

Cela implique de prendre en compte tout ce qui peut modifier les états internes du contrôle, la cohérence de la machine logique lors de son fonctionnement. Que les changements d’état soient le fait de l’utilisateur, d’une animation créée par le designer ou de tout autre acteur, peu importe.

Il faut oublier le visuel qui d’ailleurs n’existe pas forcément (la séparation du travail design / codage est telle qu’on peut aujourd’hui commencer un projet par une réflexion purement conceptuelle et graphique avec un designer plutôt que d’écrire le cahier des charges avec un informaticien…).

Repérer les ilots d’états (qui deviendront ou non des groupes d’états dans la partie visuelle), le ou les graphes de changement d’état, vérifier quels sont les graphes d’états interconnectés (et qui forment un groupe ou des sous-groupes) de ceux qui sont totalement indépendants, tout cela est essentiel.

L’initialisation

On retrouve ici une préoccupation déjà évoquée quand nous parlions de la partie visuelle. Pour le designer il s’agissait de ne pas confondre l’état Base avec les états par défaut qu’il faut créer pour chaque groupe d’états.

Côté code il est indispensable que toute nouvelle instance d’un contrôle se “positionne” correctement. Normalement son initialisation comporte d’une façon ou d’une autre des valeurs par défaut. Sous C# il est possible d’atteindre cet objectif de multiples façons : soit par le biais de champs initialisés lors de leur déclaration, soit par le biais de valeurs par défaut mises en place dans les métadonnées des propriétés de dépendance, soit par code dans le constructeur de la classe, soit par code dans le gestionnaire de l’événement Loaded du contrôle (ou de l’une de ses sous-parties), soit encore par code XAML.

Le fait que ces différentes solutions puissent être utilisées simultanément dans un même contrôle n’aide pas forcément à rendre les choses claires… C#, comme tout langage, n’interdit pas le code spaghetti, hélas !

Mais ici en dehors de la pure stylistique, de l’académisme, voire même des bonnes pratiques, c’est aussi la stabilité visuelle qui risque d’être compromise si le contrôle ne s’initialise pas clairement dans une suite d’états bien déterminés (généralement les états par défaut).

Un contrôle a ainsi la charge lors de son initialisation de se positionner sur la case “départ” de son graphe d’états. Cela semble évident mais parfois les portes ouvertes sont celles qui méritent le plus d’être enfoncées. Tout le monde peut voir qu’une porte est fermée. Il faut être très perspicace pour s’apercevoir que l’absence d’un obstacle ne signifie pas qu’il n’y a rien à faire…

Car si rien n’est fait, le contrôle va être frappé d’une sorte de schizophrénie : il peut être doté d’un code qui s’initialise correctement et d’une interface qui en fait tout autant, hélas les deux personnalités existent simultanément et ne se connaissent pas forcément. Les cas de personnalités multiples sont passionnants en psychanalyse (je vous conseille d’ailleurs un excellent vieux livre écrit par une patiente atteinte de ce syndrome : Joan, autobiographie d’une personnalité multiple) et même s’il est plus facile de déboguer un programme qu’un cerveau humain, ce type de désordre du comportement ruine totalement les efforts pour créer une interface riche et cohérente… Alors autant y penser ! Cela signifie dans la pratique que vous devez centraliser l’initialisation logique de votre contrôle (l’ensemble de ses états) et l’initialisation visuelle. Cette dernière s’effectue en général en appelant GotoState avec le paramètre d’animation à false (il s’agit d’une bonne pratique. On évite d’animer les contrôles lorsqu’ils s’initialisent). Ainsi, code et visuel sont initialisés conjointement et sont en phase.

Encore une fois ne prenez pas Base pour un état. Notamment il n’existe aucune transition gérée par le VSM entre Base et les autres états. Si votre contrôle n’est pas volontairement initialisé dans ses états par défaut aucune animation ne sera jouée lors du premier changement car Base vers un état ne génère aucune transition.

Conclusion

La création d’un contrôle est une opération longue et minutieuse car elle doit être réfléchie. Et plutôt deux fois qu’une : à la fois sous l’angle visuel et sous l’angle du code. Ces deux aspects bien séparés aujourd’hui introduisent la nécessité de penser à leur indispensable synchronisation pour garantir la cohérence du contrôle.

Mais avec un peu d’habitude on s’aperçoit bien vite qu’il est mille fois plus facile de créer un nouveau contrôle visuel avec Blend (que cela soit pour Silverlight ou WPF) qu’avec les technologies précédentes. Alors cela vaut bien un peu d’attention au départ !

Bonne conception !

N’oubliez pas que seul Blend (qui hélas n’existe pas en version express gratuite) permet sérieusement de travailler sous Silverlight et WPF. N’hésitez pas à acheter ce complément indispensable ou à vous le faire offrir (après tout, un Blend 3 pour Noël c’est mieux qu’une cravate ou une bague – pas de sexisme sur Dot.Blog! – pensez-y ! )

Et Stay Tuned !

Lequel est le plus foncé sous WPF/Silverlight : Gray ou DarkGray ?

La réponse n'est pas forcément celle que vous pensez !

La taille du cul des vaches 

Rappelez-vous, il y a de de cela plusieurs années circulait sur Internet une histoire (vraie ou fausse peu importe) qui démontrait que le diamètre des fusées de la Nasa dépendait, par une suite invraisemblable de relations de cause à effet, de la taille du cul des vaches au temps des romains. On passait par la taille des charriots dont la largeur dépendait justement de celle du fessier des ces honorables bovins, ce qui imposait une distance entre les roues, elles-même créant des traces au sol qui firent que les premières voitures à cheval, pour emprunter les routes aux ornières profondes tracées par les charrues se devaient de respecter la même distance inter-roue, etc. On en arrivait ainsi au diamètre des fusées qui, pour passer sur les trains, qui devaient passer dans les tunels, dont la taille dépendait etc, etc... Au final un objet ultra technologique et on ne plus moderne se retrouvait à respecter une taille qui dérivait de celle du cul des vaches des romains...

Je n'ai pas pu retrouver cette histoire le Web pour vous mettre la référence, si un lecteur la connaît qu'il soit sympa et qu'il laisse le lien en commentaire...

Une histoire plus vraie qu'on le pense 

Bien entendu je n'ai jamais pu savoir à l'époque s'il s'agissait d'un hoax, d'une véritée romancée ou bien d'une vérité historique. Mais l'histoire que je vais vous raconter aujourd'hui me fait penser, des années plus tard, que cette histoire était probablement vraie...

Je tire librement inspiration d'un billet publié en 2006 par Tim Sneath, ceux qui désirent lire ce billet original (en anglais) n'ont qu'à cliquer ici.

Les couleurs sous WPF / Silverlight

24 bit RGB 

En Xaml il existe plusieurs façons d'exprimer une couleur. Une des options est d'utiliser la notation 24 bits hexadécimale de type RGB (Red/Green/Blue, rouge/vert/bleu) :

<Rectangle Fill="#B2AAFF" Stroke="#10F2DA" ... />  

32 bit ARGB 

Comme WPF et Silverlight savent gérer la transparence via le canal dit Alpha, on peut spécifier une couleur par un code hexadécimal 32 bits dit A-RGB (Alpha / RGB) :

<Line Stroke="#7F006666" ... />

Ce qui donnera une ligne en Cyan à 50% d'opacité.

scRGB

Plus subtile et bien moins connu (et encore moins utilisée) est la notation scRGB qui permet d'exprimer une couleur sous la forme de 4 nombres décimaux de type Single ce qui autorise la représentation d'un gamut ultra large. A quoi cela peut-il servir d'utiliser un système de notation des couleurs qui dépasse de loin les limites du RGB qui va de #000000 à #FFFFFF ? Créer un noir plus noir que le noir ou un blanc plus blanc que blanc, à part si on est on un publiciste en mal d'argument pour une nouvelle lessive, cela semble idiot.

Et pourtant cela ne l'est pas. Il n'y a qu'en matière de lessive que "plus blanc que blanc" est une ânerie (dont Coluche se délecta dans un sketch célèbre). Lorsqu'on parle infographie cela peut avoir un sens très concret : conserver les couleurs originales si celles-ci doivent subir de nombreux traitements, comme par exemple une correction du contraste ou de la Hue (teinte dans le système HSL). En effet, à force de calcul, d'arrondis et d'approximations, votre filtre de correction peut finir par créer des à-plat horribles. En utilisant le système scRGB, le code (qu'il soit C# ou XAML) pourra conserver le maximum d'information sur chaque composante couleur.

Un exemple de notation scRGB :

 <Rectangle Stroke="sc#1, 0.6046357, 0.223508522, 0.182969958" Fill="sc#1, 0.7785599, 1, 0"
      RadiusX="25" RadiusY="25" Width="250" Height="80" StrokeThickness="5" Margin="60" /> 
 

(cela donne un rectangle jaune à bords arrondis bistres).

Les couleurs nommées

Enfin, il est possible d'utiliser des noms pour définir des couleurs.

WPF et Silverlight divergent sur ce point car, économie de code oblige pour le Framework réduit de Silverlight, ce dernier ne contient pas toutes les définitions de couleur de son aîné WPF. Mais le principe reste rigoureusement le même (je vous joint d'ailleurs en fin d'article un code qui définit toutes les couleurs WPF utilisables sous Silverlight).

On peut ainsi utiliser des noms tels que : Green, SteelBlue ou MediumVioletRed. Ce qui donne en XAML :

<Rectangle Stroke="yellow" Fill="red" Width ="50" Height="50" />

Et le cul des vaches ?

C'est là que l'affaire devient croustillante... Attendez la suite pour juger !

Par souci de compatibilité avec HTML et SVG, Microsoft a repris la liste des couleurs définies dans ces standards. Une bonne idée, on a souvent accusé Microsoft de ne pas respecter les standards, ce que j'ai toujours trouvé idiot puisque justement l'innovation vient de ce qui est différent et non de l'uniformisation. Et bien justement, quand les développeurs de chez Microsoft se plient à cette servile obligation, cela donne des choses bien curieuses (à l'insu de leur plein gré comme disait l'idiot pédaleur).

En effet, la liste des couleurs HTML est pleine de bizarreries et d'idiosyncrasies qu'il eut été préférable de corriger. Expurgée de ces annomalies la liste des couleurs HTML aurait pu faire une belle liste pour XAML, mais voilà, compatibilité oblige (en fait rien ne l'obligeait, sauf les habitudes), on se retrouve dans l'un des environnements de développement le plus moderne à trainer des bêtises d'un autre âge ! La fameuse taille du cul des vaches au temps des romains influençant le diamètre des fusées de la Nasa...

Par exemple, le spectre couvert par les couleurs HTML ne respecte pas même une répartition à peu près homogène, ce qui fait qu'on dispose de très nombreuses teintes dans les rouges ou les oranges alors que les verts sont très mal couverts.

Autre exemple, les couleurs ont parfois des noms ésotériques qui montrent à quel point les auteurs originaux n'avaient aucune volonté de rendre l'ensemble compréhensible. En dehors d'un américain pure souche, qui peut bien en Italie, en France ou en Slovénie s'imaginer de quel bleu il s'agit lorsque HTML nous indique un "DodgerBlue" ? La charte couleur des Tshirts d'une équipe d'un sport totalement inconnu (le baseball) chez nous n'évoque rien (les dodgers sont en effet une équipe de baseball de Los Angeles, très connus certes, mais uniquement des américains et des rares amateurs étrangers).

Des origines encore plus lointaines

Mais si cela s'arrêtait là le rapprochement avec la petite histoire sur l'arrière train des vaches pourrait sembler un peu capilotractée. En fait nous sommes exactement dans le même cas. Car les choses ne s'arrêtent pas à HTML. Ces couleurs remontent en réalité aux premières implémentations sous UNIX du système X-Window ! HTML définit 16 couleurs qui sont directement mappées sur les 16 couleurs de la palette EGA. Mais plus loin encore, les premiers browsers comme Mosaic (1992) supportaient aussi les couleurs nommées de X11 ! Malheureusement certaines couleurs HTML avaient des homonymes X11 qui, bien entendu, ne donnait pas exactement la même teinte... par exemple ce vert HTML donnait ce vert sous X11.

Un gris plus foncé que le gris foncé

Et c'est ainsi que de tout ce mélange le Gray HTML fut défini par #808080 alors que le DarkGray est défini par  #A9A9A9, un gris plus clair que le gris...

On en revient à la question posée dans le titre de ce billet. Et vous voyez que la réponse est loin d'être celle qui semble s'imposer en toute logique !

WPF et Silverlight réutilisent cette liste de couleurs qui ne remonte pas aux temps des romains, mais pour l'informatique, l'époque de X11 c'est même pire : de la préhistoire !

Du coup, il semble bien plus intelligent d'utiliser les codes couleurs RGB, ARGB ou scRGB que les couleurs nommées si on ne veut pas obtenir des résultats étranges...

Incroyable non ?

On pourrait tirer milles conclusions de cette petite histoire. Je vous laisse y réfléchir, je préfère ouvrir des portes que d'asséner des jugements définitifs. Poser des questions ou créer le questionnement est souvent bien plus utile que d'apporter des réponses toutes faites.

On peut aussi juste en rire, c'est bon pour la santé :-)

Stay Tuned !

(pour ceux qui ont lu jusque là, le cadeau annoncé : ColorHelper.cs (16,31 kb) )

Silverlight : Enum.GetValues qui n'existe pas, binding et autres considérations

Silverlight, comme vous le savez, propose dans quelques méga octets un mini framework .NET qui est tellement bien "découpé" que la plupart du temps on ne se rend même pas compte qu'il manque des dizaines de méga de code binaire... Pourtant entre une installation complète du Framework 3.5 ou 4.0 à venir et l'installation du plugin Silverlight il y a comme une énorme différence ! De deux choses l'une, soit Silverlight ne sait rien faire tellement il est diminué par ce découpage, ce qui n'est pas le cas, soit le Framework complet est juste gonflé avec des fichiers inutiles pour "faire sérieux", ce qui n'est pas le cas non plus :-)

Un découpage savant 

Donc ce n'est ni l'un ni l'autre. La troisième possibilité, qui est la bonne réponse, est que le Framework complet est d'une richesse infinie dans les moindres détails, et que le Framework Silverlight "ruse" en zappant beaucoup de détails mais sans perdre l'essentiel. Cela donne l'impression qu'on peut tout faire en Silverlight comme en WPF. C'est "presque" vrai. Assez rapidement on tombe sur les fameux petits détails qui manquent. Cela implique de les compenser par du code.

Cela dit loin d'être une critique négative de Silverlight s'en est au contraire une apologie ! Je trouve en effet le découpage savant qui a été effectué dans Silverlight particulièrement bien fait. L'approche est très sensée : il est rarissime (même impossible) qu'une application utilise et nécessite d'accéder à toutes les méthodes, toutes les propriétés de toutes les classes du Framework tellement celui-ci est riche. Du coup, en faisant des coupes bien choisies, on peut laisser les squelettes de presque tout le Framework ainsi que les principales méthodes et propriétés utilisées le plus souvent. On obtient le Framework Silverlight dans lequel un code standard trouvera 95% de tout ce qu'il lui faut pour tourner. Beaucoup d'applications simples ne verront même pas qu'il manque quelque chose. En revanche pour les autres cas, le développeur ajoutera les contournements nécessaires ce qui grossira un peu son code binaire Silverlight, mais d'une petite fraction très supportable. Rien à voir avec le coût d'une installation du Framework complet.

Il n'en reste pas moins vrai que parfois pour des choses très simples on se retrouve un peu le bec dans l'eau. "Tiens, c'est bizarre, j'aurais juré que la méthode xxx existait !" Et non, on n'a pas rêvé, elle existe bien, mais dans le Framework complet, pas dans celui de Silverlight. Un exemple tout simple : la méthode GetValues() de la classe Enum.

Enum.GetValues où es tu ?

Un cas d'utilisation très basique qui fait voir immédiatement qu'il manque quelque chose : essayez de faire un binding entre une combobox et une énumération. Truc classique par excellence.

Que le binding soit fait par Xaml ou bien par code, à un moment où un autre il faut que l'énumération sache retourner la liste de ses valeurs. C'est justement la fonction de la méthode Enum.GetValues().

Mais dans le Framework Silverlight cette méthode n'existe tout simplement pas. Victime de la cure d'amaigrissement évoquée plus haut. Il ne s'agit donc ni d'un oubli ni d'un dommage collatéral, c'est un parti pris, assumé.

Et alors on fait comment ?

Assumé, assumé... comme il y va ! Non, je vous l'assure, c'est assumé. Par l'équipe qui développe le Framework Silverlight en tout cas. Mais pas forcément par les développeurs qui l'utilisent ! Car pour eux c'est une autre histoire puisque, en effet, il va falloir réinventer cette méthode.

A l'aide d'un peu de Linq to Object et de Reflexion, on peut s'en sortir.

Linq et Reflexion

Il existe en effet un moyen d'obtenir les valeurs d'une Enum par la réflexion, en utilisant la méthode GetFields() sur le type de l'Enum dont on souhaite obtenir les valeurs. 

GetFields() retourne un tableau de FieldInfo. Une Enum présente alors ses différentes valeurs comme un tableau de champs. En plus de ces champs, GetFields() retournera aussi des éléments qui ne sont pas des valeurs de l'énumération mais d'autres champs de la classe. Au sein de FieldInfo vous trouverez un ensemble de méthodes nommées Isxxx(), l'une d'entre elles nous intéresse plus particulièrement ici; c'est IsLiteral. Toutes les valeurs de l'énumération retournent True. La solution est alors simple en ajoutant à la Réflexion un peu de Linq to Object :

   1:  var enumType = typeof(monEnumeration);
   2:  var fields =  from field in enumType.GetFields()
   3:                where field.IsLiteral
   4:                select field.GetValue(null).ToString();
   5:  LaCombobox.ItemsSource = fields;

A partir du type de l'énumération (ligne 1) on construit une requête Linq to Object qui filtre tous les champs ayant IsLiteral à vrai et qui sélectionne la valeur de ce champ sous la forme d'une string.

Ne reste plus qu'à faire le binding entre cette requête Linq et ItemsSource de la combo box.

Il faudra ajouter un peu de code pour transformer la chaîne sélectionnée dans la combo en une valeur de l'énumération grâce à un appel à Enum.Parse().

C'est la version simple et courte. Bien entendu dans le cas où on souhaite faire du binding plus automatisé, notamment directement en Xaml, la solution donnée ici est un peu trop simple. L'esprit est le bon mais il manque des petites choses comme un convertisseur de valeurs.

D'autres versions plus sophistiquées

Il est bien sûr possible d'aller plus loin et de formuler une solution plus sophistiquée qui permettent de faire du binding en Xaml notamment. Je vous laisse y réfléchir, ça fait un bon excercice C# et ce n'est pas un MVP C# qui vous dira que s'entraîner mentalement de la sorte sur le langage est inutile ! :-)

Mais sachez que d'autres y ont déjà pensé et ont proposé des solutions souvent assez proches (ce problème ne peut pas être résolu de dix milles façons). En voici quelques unes dans lesquelles vous pourrez piocher matière à aller plus loin :

Silverlight c'est sympa et en plus ça fait travailler les méninges, que du bon ! 

Stay Tuned !

 

 

 

Silverlight : Contrôle et zone cliquable, astuce...

Une petite astuce toute simple pour aujourd'hui (une fois que l'on connaît la réponse).

Un contrôle très basique pour commencer

Prenons un contrôle, un UserControl carré avec un joli cercle à l'intérieur. Pour que tout cela soit facile à comprendre et à tester, disons que le UserControl fait 200x200 pixels, et que l'ellipse à l'intérieur remplie toute la zone (c'est un cercle donc). Cette ellipse aura un Stroke noir par exemple et une épaisseur de trait de 12. Le LayoutRoot sera un Canvas.

Donc, nous avons : un UserControl de 200x200 contenant un Canvas (toutes les propriétés par défaut), ce Canvas contient une ellipse qui remplit la zone du contrôle, l'ellipse possède un Stroke de 12 en noir. En gros nous avons dessiné un cercle noir sur fond transparent.

"Hou la la ! Mais c'est du super haut niveau tout ça !" ... On ne s'inquiète pas, ça va très vite se compliquer ...

Première petite amélioration : nous allons mettre un curseur personnalisé à notre contrôle. Dans les propriétés du UserControl, propriété Cursor, on choisit Hand, (la main). Et on exécute (F5).

Ca se complique...

Passer la souris au-dessus de votre magnifique contrôle : la main s'affiche bien, mais uniquement lorsque votre souris survole le Stroke du cercle ! Rien à l'intérieur.

Qu'est ce qu'il se passe ?

Pour ce que j'en comprends, tout ce qui n'a pas une brosse n'existe pas. Notre contrôle et le Canvas sous-jacent n'ont pour l'instant aucune couleur de fond (Background). L'ellipse n'a pas de Fill non plus. Tous ces espaces "sans rien" ne sont visiblement pas détectés. Bug ou feature, après tout ça peut être l'un ou l'autre selon le point de vue...

Mais comment régler le problème ?

En fait qu'importe qu'il s'agisse d'un problème ou d'un choix délibéré des concepteurs, nous devons remplir ces "riens" si nous voulons que le curseur de souris (et la zone cliquable du contrôle) apparaisse sur toute la surface.

Mais il est hors de question d'aller placer une brosse, donc une couleur, pour faire ce travail ! On pourrait bien entendu mettre un Background blanc au Canvas, cela règlerait le problème. Mais si nous changeons le fond de notre page, nous allons obtenir un cercle dans un carré blanc sur un fond différent. Une horreur. Quant à s'enliser dans l'erreur pour faire en sorte de trouver une astuce pour que le fond de notre UserControl soit synchro avec celui de son conteneur parent...

Une "fausse" brosse

Il faut remplir notre ellipse pour que le curseur soit visible et que même son espace intérieur soit cliquable. Mais pas avec n'importe quoi. Xaml nous fournit une brosse un peu spéciale, disons une "non brosse", ou une "fausse" brosse puisque visuellement elle ne se voit pas. Il s'agit de la brosse "Transparent".

Dès lors, il suffit d'ajouter dans le code Xaml de notre ellipse Fill="Transparent" et de relancer l'application. Miracle ! Le curseur en forme de main est visible même à l'intérieur de l'ellipse. Et si nous changeons la couleur de fond de la page, comme cette brosse est transparente, on n'y verra que du feu ...

Sous Blend vous pouvez agir directement dans le code Xaml ou bien simplement cliquer sur le carré de binding de la propriété Fill et choisir "custom expression". Tapez "Transparent" (sans les guillemets) dans le champ de saisie et l'affaire est jouée.

Comme quoi, même un UserControl ne contenant qu'un cercle peut déjà soulever des problèmes pas si simples à résoudre. Ca force à l'humilité... :-)

Stay Tuned !

Création de jeux et détection de touches clavier multiples sous Silverlight

Silverlight est conçu pour s'intégrer dans une chaîne de développement "sérieuse". Son but est principalement la création de RIA (Rich Internet Application) le plus souvent associées à des applications métier. Il s'agit en fait de porter WPF sur le Web et de faire ainsi converger applications "desktop" et applications Web de façon transparente. Mais Silverlight est aussi un merveilleux outil pour écrire des jeux en ligne (ou même desktop grâce à la fonction Out-Of-Browser). Plus loin, et j'en parle souvent, les techniques visuelles du jeu ne sont pas à négliger pour créer des applications professionnelles disposant d'interfaces innovantes.

Raison de plus pour se pencher sur la question. Dans les jours passés je vous ai parlé de la gestion du son (notamment en vous proposant un petit composant capable de rendre possible la synchronisation son/animation - AnimatableSoundPlayer. Ou comment synchroniser du son sur une animation Silverlight et Animations, Sons, Synchronisation, Ease in/out sous Blend), aujourd'hui je vais aborder la gestion du clavier. Pour ce qui est de la boucle de jeu, je vous renvoie à mon billet exposant l'exemple de la neige qui tombe (« Il neige » ou les boucles d’animation, les fps et les sprites sous Silverlight ).

D'abord un petit exemple live. Une fois que vous aurez cliqué sur le rectangle "click here to start" vous pourrez déplacer le carré rouge et changer sa taille. Les déplacements se font à l'aide des 4 flèches de direction. Pour agrandir l'objet on utilise la combinaison de touche Shift-KeyUp (et Shift-KeyDown pour diminuer la taille). La touche Ctrl est un accélérateur pour les mouvements. Ctrl-KeyUp/Down/Left/Right permet donc d'aller plus vite. Ce qui est intéressant dans cet exemple est bien entendu la détection des appuis simultanés sur les touches et la gestion de ceux-ci. Jouer deux secondes et on y revient :

[silverlight:source=/SLSamples/KeyTrapper/KeyTrapper.xap;width=400;height=400]

Tout le problème ici est donc de détecter plusieurs touches appuyées en même temps. Silverlight propose une gestion de clavier très simple : les événements KeyDown et KeyUp. Avec ça il faut se débrouiller.

Pour une gestion de déplacement rudimentaire (les 4 directions par les 4 flèches) cela pourrait parfaitement convenir. Mais comment gérer les déplacements en diagonal (KeyUp+KeyLeft par exemple) ou pire des choses du genre Shift-Ctrl-KeyUp/Barre d'espace ? Il faudrait se rappeler dans l'événement KeyDown si l'une ou l'autre des touches a été enfoncée et si elle est toujours Down... De plus aucune assurance de l'ordre dans lequel les touches vont arriver (la simultanéité n'existe pas du point de vue du pilote clavier, une touche est forcément avant l'autre).

Bref cela semble d'un seul coup se compliquer énormément !

Oui et non. Oui puisque la solution n'est pas directe, non parceque celle-ci n'est pas si compliquée à trouver.

La classe KeyLogger

Ne cherchez pas dans l'aide du Framework Silverlight, cette classe n'existe pas :-) Il s'agit justement de la classe que je vous propose aujourd'hui de créer pour gérer le petit problème des touches simultanées.

Problématique

On doit pouvoir à tout moment dans la boucle principale du jeu tester si certaines combinaisons de touches sont actives ou non. On doit aussi pouvoir tester l'appui sur une seule touche.

Solution

Il n'y a pas de miracle, pour savoir si une ou dix touches sont appuyées il faut se souvenir de toutes les touches ayant reçu un KeyDown et pas encore de KeyUp. Se souvenir, en informatique, cela signifie stocker des données. C'est bien ce que nous allons faire en créant un tableau dont les éléments seront des booléens et dont l'index sera la valeur de l'énumération Key (classe du Framework listant les touches accessibles du clavier). Ne reste plus qu'à gérer nous-mêmes les KeyUp et KeyDown pour mettre à vrai/faux les cases correspondantes dans le tableau. Pour tester une combinaison de touches (ou une touche) il suffit alors d'interroger le tableau pour savoir si les touches que nous voulons tester sont toutes à true.

Le code

Une fois le problème et la solution posés, il ne reste plus qu'à coder. Plutôt que d'alourdir ce post, je vous renvoie au code source du projet téléchargeable en fin de billet. Ouvrez-le sous Blend ou Visual Studio, vous comprendrez rapidement comment il fonctionne.

Le UserControl principal de l'application exemple commence par créer une instance du KeyLogger et l'attache à lui-même. On pourrait fort bien attacher un KeyLogger différent à différentes zones écran, ici nous optons pour un seul gestionnaire centralisé.

La détection du clavier ne marche que si le contrôle a le focus. Donner par défaut celui-ci au UserControl n'est pas forcément évident, je n'ai pas trouvé de solution qui marche systématiquement. D'où la petite astuce du bouton de démarrage : en cliquant dessus je lance l'animation qui le fait disparaître et qui diminue l'opacité des instructions mais surtout j'en profite de réclamer le focus. Cette technique a l'avantage de fonctionner à tous les coups.

Conclusion

Remplacer le rectangle rouge par une petite fusée, ajoutez des blocs rocheux, détectez la barre d'espace pour tirer, utilisez la gestion de sprites pour animer les tirs de laser, agrémentez d'une gestion de collision, complétez par une gestion de l'inertie des mouvements... et vous aurez une réplique de Asteroid ! Yaka. :-)

Déplacer des objets ou contrôler des animations via le clavier est l'une des bases de la création de jeux. Avec l'exemple de la neige qui tombe nous avions déjà vu la gestion des sprites, vous disposez maintenant des briques pour commencer à réfléchir à des petits jeux sympas (éviter la cent-millième version de Asteroid ou de Space Invader bien entendu :-) )!

Amusez-vous bien, et .... Stay Tuned !

Code source du projet de la classe KeyLogger : KeyTrapper.zip (78,30 kb)