Dot.Blog

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

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 !

Silverlight 4 : Mamma mia !!!

La bêta de SL 4 arrive… Et les nouveautés sont encore une fois époustouflantes faisant de cette prochaine version une release majeure comme les 3 précédentes et non une simple mise à jour.

Certains seront déçus d’apprendre que la vraie 3D à la WPF ne semble pas encore intégrée (mais d’ici la finale peut-être ?), en revanche sur tous les autres points les améliorations sont extraordinaires et gomment, une fois encore et comme je le dis depuis longtemps, toutes les différences essentielles entre une application WPF de bureau et une application Silverlight Web.

Silverlight 4 c’est le petit poucet qui mange l’ogre, c’est le chaperon rouge qui se farcit un gigot de loup aux herbes. L’intérêt de WPF ne tient plus qu’à quelques fils que savamment et avec un doigté exceptionnel l’équipe de Silverlight coupe un à un …

Depuis un moment je vous dis mon sentiment que le futur de Xaml s’appelle Silverlight et chaque nouvelle release nous rapproche de cette fusion entre desktop et web, au point que, j’en suis convaincu, il arrivera un jour prochain où une seule technologie sera maintenue. Et elle ne s’appellera pas WPF, mais Silverlight. Donc, si vous ne vous y êtes pas encore mis, il est plus que largement le temps. Ceux qui ont suivi mon conseil en 2005 de virer de bord pour C# au lieu de Delphi ne l’ont pas regretté, même si beaucoup étaient très sceptiques à l’époque… Si je vous dis aujourd’hui faites du Silverlight, faites comme vous le sentez mais je me trompe que rarement sur ces choses là !

Les nouveautés orientées applications

  • Support d’impression permettant les hardcopies (copie d’écran) et la sortie de documents autant que la visualisation virtuelle des impressions (preview), indépendamment du contenu de l’écran.
  • Un nouvel ensemble de composants pour les fiches de saisie avec 60 contrôles personnalisables
    • dont des contrôles tels qu’un éditeur Rich Text avec support des hyperlinks, des images, des Textbox masqués pour les validations complexes…
    • DataGrid améliorée avec tri et resize des colonnes et le copy/paste des lignes
  • Intégration de WCF RIA pour créer des applications n-tiers avec support transactionnel, paging, etc.
  • Extension des possibilités de localisation avec support bi-directionnel et accès à 30 nouvelles langues comme l’hébreu, l’arabe, le thaï, etc.
  • Le CLR permet maintenant que le même code compilé puisse tourner aussi bien en mode Web qu’en mode Desktop !
  • Databinding amélioré avec grouping / editing et formatage de chaines à l’intérieur du binding
  • Support complet de M.E.F. pour créer des applications composites de grande taille (chargement dynamique, découverte automatique des plugins, etc).
  • Support complet de la surface de design dans Visual Studio 2010 et meilleure intégration avec Blend.

Les nouveautés orientées outils dans VS 2010

  • Surface de design totalement éditable
  • Une property grid riche avec de nouveaux éditeurs de valeurs
  • Drag’Drop support pour le Databinding, nouvelles fenêtres pour les datasources,…
  • Simplification des accès aux styles définis dans Blend
  • Intellisense pour Xaml, C# et VB

Les nouveautés améliorant l’UX

  • Support des Webcam et des micros pour le partage de vidéos et de l’audio par exemple pour créer des applications de type Chat ou Assistance client (ou bien d’autres choses !)
  • Enregistrement local de vidéos et de sons. Capture video Raw sans interaction avec un serveur (pour créer des applis de vidéos conférence par exemple).
  • Insérer des données externes avec Copy, Paste ou Drag’drop
  • Les listes gèrent la molette souris
  • Support des interactions conventionnelles du bureau avec par exemple les menus locaux par clic-droit
  • Support de Google Chrome
  • Optimisation des performances : les applications SL4 démarrent plus vite et s’exécutent 200% plus vite que leur équivalent SL3 !
  • Support du Multi-touch et des “gestures” (mouvements pré-établis ayant une signification dans le logiciel comme par exemple dessiner un rond sur l’écran avec le doigt pour déclencher un traitement)
  • Support du Multicast
  • Protection du contenu H.264 au travers des DRM Silverlight (utilisant PlayReady)
  • Protection des sorties pour l’audio et la video permettant au propriétaire du contenu ou au distributeur de s’assurer que les médias sont diffusés au travers d’une connexion vidéo sécurisée

Les nouveautés out-of-browser

Pour les applications dans la sandbox

  • Possibilité de placer du HTML dans les applications pour une plus grande intégration avec du contenu web comme les mails, l’aide, les états…
  • Support des notification “toast”, c’est à dire des fenêtres indiquant le statut de l’application même si l’utilisateur utilise une autre application
  • DRM offline
  • Contrôle de la position de la fenêtre, sa taille, son style

Pour les applications “trusted”

  • lecture / écriture depuis l’application SL des emplacements Mes Documents, Ma Musique, Mes Images, Mes Vidéos (ou leurs équivalents pour les machines non Windows). Permet le stockage et la lecture locale en dehors de l’Isolated Storage pour une meilleure intégration avec les données de l’utilisateur (production et stockage d’états par exemple)
  • Appel d’autres programmes Desktop comme Office par exemple. Il devient possible entre autre d’ouvrir Outlook pour envoyer un mail, de générer un état qui s’ouvre dans Word ou des tableaux de données qui s’affichent dans Excel, etc.
  • Automation COM autorisant l’accès aux périphériques et aux autres possibilités du système hôte. Par exemple : accès à un lecteur de carte d’accès ou de badges en USB.
  • Une nouvelle interface utilisateur pour les demandes de privilèges lorsque l’application fonctionne out-of-browser.
  • Objets de Group Policy permettant aux entreprise de fixer des droits globaux aux applications Silverlight tournant out-of-browser pour un déploiement simplifié et sécurisé.
  • Accès au clavier en mode FullScreen (plein écran)
  • Amélioration de l’accès à des données cross-domain sans avoir besoin de la présence d’un fichier de sécurité

Conclusion

Toutes ces nouveautés ouvrent des horizons radieux aux applications Silverlight et minimisent encore plus la distance entre applications de bureau et applications web.

On appréciera l’accès au clavier dans le mode plein écran qui permettra de faire des applis plus belles, plus immersives pour l’utilisateur. On notera avec intérêt l’accès au hardware ouvrant par exemple la possibilité pour les logiciels médicaux d’accéder à un lecteur de carte Vital tout en créant des applis Full Web (je sais que plusieurs de mes clients éditeurs seront vivement intéressés !). On piaffe d’impatience devant les nouvelles possibilités de preview et d’impression qui manquaient cruellement.

On appréciera bien entendu en même temps que les efforts “cosmétiques” tous les efforts techniques de cette version (MEF, RIA data services…) faisant de Silverlight un outil “pro” pour créer des applications “pros”. Microsoft a résolument décidé de laisser le côté amateur des outils permettant tout juste de créer des bandeaux de pub à la concurrence…

Bref, chaque nouvelles possibilités de Silverlight 4 créée un champ infini de nouvelles cibles potentielles : applications de gestion (facturation, suivi commercial), de support (Chat, Assistance technique), médical, multimédia (vidéos, sons)…

Nous assistons à chaque release de SL à la maturation d’une technologie majeure qui va s’imposer au fil du temps comme “la” technologie pour créer des applications riches, légères ou non, Web ou Desktop.

Quelle époque formidable ! :-)

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 !

 

 

 

44 Videos US et 11 Ateliers en français sur Silverlight !

Dans un billet de 2008 je vous parlais de l'extraordinaire travail publié par Mike Taulty44 vidéo autour de Silverlight (V 2 à l'époque) ! Cette série reste toujours d'actualité pour se former, mais elle peut gêner ceux qui ne pratiquent pas assez bien l'anglais pour capter chaque leçon. Si j'en reparle c'est que les liens que j'avais donnés à l'époque ne sont plus bons et qu'il était nécessaire de les réactualiser (grâce à la vigilance d'un lecteur que je remercie au passage !). C'est chose faite dans le présent billet.

Mais j'en profite aussi pour vous parler d'une superbe série d'ateliers que Microsoft vient de mettre en ligne. Le coach Silverlight (David Rousset bien connu pour son blog notamment) a beaucoup travaillé cet été pendant que vous profitiez de la plage ! Il était prévu à l'origine que les 12 ateliers soient mis en ligne progressivement (David veut écrire un petit topo avec chaque module et ça réclame un peu de temps). Mais devant le succès des premiers modules et l'insistance des lecteurs, Microsoft a décidé de mettre tous les ateliers en ligne, David complètera les modules de leur petit topo au fur et à mesure.

Si on compte bien, 44 + 11 ça donne 55 modules de formation gratuits pour prendre en main Silverlight... Les longues soirées d'automne vont être chargées et vous pouvez d'ores et déjà prévoir de laisser votre Wii à vos enfants, vous n'allez pas beaucoup y toucher dans les semaines à venir !  

L'accès direct aux sommaire des 44 casts US : http://misfitgeek.com/blog/44-silverlight-videos/

Le sommaire pour se mettre l'eau à la bouche :

  1. Silverlight - Hello World
  2. Silverlight - Anatomy of an Application
  3. Silverlight - The VS Environment
  4. Silverlight - Content Controls
  5. Silverlight - Built-In Controls
  6. Silverlight - Width, Height, Margins, Padding, Alignment
  7. Silverlight - Using a GridSplitter
  8. Silverlight - Grid Layout
  9. Silverlight - StackPanel Layout
  10. Silverlight - Canvas Layout
  11. Silverlight - Databinding UI to .NET Classes
  12. Silverlight - Simple Styles
  13. Silverlight - Custom Types in XAML
  14. Silverlight - Binding with Conversion
  15. Silverlight - List Based Data Binding
  16. Silverlight - Simple User Control
  17. Silverlight - Templating a Button
  18. Silverlight - Resources from XAP/DLL/Site Of Origin
  19. Silverlight - Animations & Storyboards
  20. Silverlight - Uploads with WebClient
  21. Silverlight - Downloads with WebClient
  22. Silverlight - Calling HTTPS Web Services
  23. Silverlight - Calling Web Services
  24. Silverlight - Making Cross Domain Requests
  25. Silverlight - Using HttpWebRequest
  26. Silverlight - File Dialogs and User Files
  27. Silverlight - Using Sockets
  28. Silverlight - Using Isolated Storage
  29. Silverlight - .NET Code Modifying HTML
  30. Silverlight - Using Isolated Storage Quotas
  31. Silverlight - Calling JavaScript from .NET
  32. Silverlight - Evaluating JavaScript from .NET Code
  33. Silverlight - Handling HTML Events in .NET Code
  34. Silverlight - Handling .NET Events in JavaScript
  35. Silverlight - Calling .NET from JavaScript
  36. Silverlight - Displaying a Custom Splash Screen
  37. Silverlight - Passing Parameters from your Web Page
  38. Silverlight - Loading Media at Runtime
  39. Silverlight - Dynamically Loading Assemblies/Code
  40. Silverlight - Reading/Writing XML
  41. Silverlight - Multiple Threads with BackgroundWorker
  42. Silverlight - Insert/Update/Delete with the DataGrid
  43. Silverlight - Getting Started with the DataGrid
  44. Silverlight - Embedding Custom Fonts

Le coach français : http://msdn.microsoft.com/fr-fr/silverlight/msdn.coachsilverlight.aspx

Et le sommaire pour vous donner encore plus envie d'y aller tout de suite :

 

Stay Tuned pour d'autres infos !

AnimatableSoundPlayer. Ou comment synchroniser du son sur une animation Silverlight

Hier je vous parlais d'animation et de sons et je vous présentais une petite démonstration (l'oscilloscope) dont le but était de montrer qu'on pouvait rapidement obtenir un effet visuel assez complexe sans programmation pour peu qu'on se donne la peine d'utiliser Blend et ses neurones... Pour synchroniser le bip avec le spot lumineux j'avais alors utilisé une ruse en indiquant qu'hélas Silverlight ne permettait pas de synchroniser du son dans une animation.

Je vous proposais alors une idée de solution en promettant une implémentation le temps de vous laisser réfléchir.

Regarder d'abord la démo ci-dessous. En cliquant sur "Start" vous lancerez une animation (StoryBoard) dont le but premier est de déplacer la boule verte un peu comme dans un billard. Elle rebondit sur des taquets pour finir dans un gobelet. Bien entendu tout cela est assez moche, les beaux dessins ne font pas partie de la question :-) Mais, en revanche, et si votre carte son fonctionne, vous noterez que plusieurs sons peuvent être entendus en parfaite synchronisation avec les "chocs" de la boule sur les taquets ou dans le gobelet final. Je vous laisse essayer et on en reparle :

[silverlight:source=/SLSamples/SyncSound/SyncSound.xap;width=251;height=242]

Bon. Vous avez vu et aussi entendu ?

Comment est-ce possible (sachant que pour l'utilisateur Blend qui créé l'animation tout cela ne réclame aucun code) ?

Forcément il y a du code... En fait un petit UserControl dont le principe est fort simple.

AnimatableSoundPlayer

C'est son petit nom. Un joueur de sons animable. Joueur de sons car ce UserControl n'a pas de visuel. On pourrait en réalité jouer des vidéos de la même façon il suffirait de relooker le contrôle, mais ce n'était pas le but. Joueur de son animable car ce qui manque au MediaElement c'est bien d'avoir des propriétés "animables" pouvant être modifiées sur une timeline.

Le UserControl AnimatableSoundPlayer est ainsi une coquille presque vide, il ne contient qu'un MediaElement. Tout le reste est du code, fort peu en réalité.

Dans un premier temps j'ai ajouté plusieurs propriétés de dépendance qui relaient les propriétés du MediaElement : la Source (une Uri), le Volume, etc. Ensuite j'ai fait de même pour les principales méthodes (Play, Stop...).

Deux choses ont été ajoutées. Côté méthodes j'ai créer PlayFromStart(). Elle ne fait que faire "Stop(); Play();". C'est tout bête mais très souvent on a besoin d'enchaîner ces deux méthodes pour s'assurer qu'un son est bien rejouer depuis le début, il faut rembobiner la bande avec Stop() pour réécouter un son.

La seconde chose ajoutée est la plus importante. Il s'agit de la propriété de dépendance AnimatablePlay de type double. Pourquoi double ? Simplement parce Silverlight sait animer des doubles et par forcément autrechose... Le mécanisme intéressant se trouve dans la méthode AnimatablePlayChanged, le callback de modification de la valeur initialisé lors de la création de la propriété de dépendance. A l'intérieur de ce callback nous trouvons la logique de cette propriété :

   1:  #region AnimatablePlay property
   2:          /// <summary>
   3:          /// Gets or sets the animatable play.
   4:          /// Value = 0 means <see cref="Stop"/>
   5:          /// Value sup.to 0 means <see cref="PlayFromStart"/>
   6:          /// Value inf.to 0 means <see cref="Play"/> / <see cref="Pause"/>
   7:          /// </summary>
   8:          /// <value>The animatable play.</value>
   9:          [Category("Media")]
  10:          public double AnimatablePlay
  11:          {
  12:              get { return (double)GetValue(AnimatablePlayProperty); }
  13:              set { SetValue(AnimatablePlayProperty, value); }
  14:          }
  15:   
  16:          public static readonly DependencyProperty AnimatablePlayProperty =
  17:              DependencyProperty.Register("AnimatablePlay", typeof(double),
  18:              typeof(SynchedSoundPlayer), new PropertyMetadata(0.0d, AnimatablePlayChanged));
  19:   
  20:          private static void AnimatablePlayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  21:          {
  22:              var mp = ((SynchedSoundPlayer)d);
  23:              if ((double)e.NewValue > 0d) mp.PlayFromStart();
  24:              if ((double)e.NewValue == 0d) mp.Stop();
  25:              if ((double)e.NewValue < 0d)
  26:              {
  27:                  if (mp.internalMP.CurrentState == MediaElementState.Playing) mp.Pause();
  28:                  else mp.Play();
  29:              }
  30:          }
  31:   
  32:          #endregion

Simple et efficace : toute valeur positive déclenche un "PlayFromStart", d'où l'utilité de cette méthode qui nous assure que le son est bien rejoué depuis le début. Toute valeur nulle appelle "Stop" et toute valeur négative entraîne un cycle Play/Pause selon l'état actuel du contrôle.

Le UserControl relaie aussi l'événement MediaFailed si on désire être prévenu en cas de difficulté rencontrée par le MediaElement pour charger le son (qui peut être dans le Xap comme dans la présente démo ou sur un serveur distant).

Grâce à cette convention il devient très simple de synchroniser des sons avec une animation !

Si on suit l'exemple live proposé plus haut : Trois sons sont joués, un numéro est indiqué sur chaque "obstacle" pour mieux se repérer. Sur ces trois sons il y en a un qui utilisé deux fois. Pour synchroniser ces trois sons on reprend la timeline de la boule verte et à chaque fois qu'on désire qu'un son soit joué on incrémente sa propriété AnimatablePlay. Simple. Vu la taille des valeurs maximales d'un double, on ne risque pas de tomber sur une limite... surtout qu'il n'est pas interdit de faire un retour à zéro quand on le veut.

Un petit détail à savoir : quand on créé une animation, par défaut Silverlight effectue une interpolation des valeurs fixées dans les keyframes qui se suivent. Si vous tapez la valeur 1 à la frame A et que vous tapez 2 à la frame B, durant le temps qui sépare A de B la valeur augmentera progressivement de 1 vers 2. C'est le comportement par défaut, ce qui est bien utile puisque justement le plus souvent on désire avoir une continuité dans le changement des valeurs (déplacements notamment). En revanche il y a des cas où on préfère que la valeur change brutalement, une "anti animation" en quelque sorte. Cela est parfois utile. Pour utiliser AnimatableSoundPlayer ce n'est pas une option, c'est une obligation. Il faut que les valeurs ne changent que lorsqu'une nouvelle keyframe est rencontrée. Sinon le son sera rejoué sans cesse depuis le début à toutes les valeurs intermédiaires, ce qui n'est pas du tout l'effet recherché.

Pour arriver à ce résultat il suffit de ne pas oublier de paramétrer les keyframes servant à animer le son afin qu'à la place d'une EasingDoubleKeyFrame le type devienne DiscreteDoubleKeyFrame. Sous Blend il suffit de cliquer sur la keyframe en question et de la passer en mode "Hold in". C'est la seule contrainte du composant.

Et voilà ! Avec un peu d'imagination on peut parfaitement créer un composant réutilisable qui ne prend que quelques lignes de code et qui permet de synchroniser du son dans une animation Silverlight. C'est pas magique ?

Je suis certain que vous trouverez des tas d'améliorations à porter au composant, alors n'hésitez surtout pas à m'en faire part je pourrais même diffuser vos versions modifiées si vous le voulez.

Le code du projet fourni contient l'exemple complet ainsi que le code du composant et les sons utilisés.

Quelques restrictions : Si vous modifiez le code obligez vous à publier le code source de votre version gratuitement. Si vous publier un article ou un billet de blog, soyez sympa et indiquez le lien vers mon billet. Si vous utilisez mon code dans un projet, commercial ou non, dites le moi ça me fera plaisir.
Si vous respectez ce petit deal, alors faites ce que voulez du code. Dans la négative que les foudres du Grand Bug Céleste s'abattent sur vous et chaque octet que vous coderez jusqu'à la fin de vos jours (c'est bien horrible ça non ? Laughing).

Enfin bon, dans tous les cas, si vous en voulez encore du Silverlight, vous connaissez le refrain : Stay Tuned !!!

Le code du projet : SyncSound.zip (209,05 kb)

Animations, Sons, Synchronisation, Ease in/out sous Blend

Effets visuels et sonores font partie des nouveaux éléments avec lesquels le concepteur de logiciel doit apprendre à jouer pour créer des interfaces attrayantes. Les réflexes du développeur le porte à se jeter sur son clavier, tout comme ceux du graphiste le pousse à sortir son bloc et son crayon. Comme tout réflexe il faut savoir s'en méfier...

Prenons l'exemple de l'affichage ci-dessous qui simule un oscilloscope :

[silverlight:source=/SLSamples/MovingSpot/MovingSpot.xap;width=288;height=237]

(passez la souris sur "settings" pour ouvrir la fenêtre de paramètres qui permet de jouer sur la lumonisité des carrés de ciblage, le flouté du spot et sur le volume du bip)

Pour créer l'effet du spot et de ses échos visuels le développeur aura donc tendance à se ruer sur son clavier et, s'inpirant des sprites (voir mon billet sur la création d'un effet de neige), il voudra créer une classe pour le spot, puis recherchera la formule mathématique de la courbe du déplacement du spot pour animer ce dernier, les échos étant créés à la volée avec une animation d'opacité décroissante. Tout cela va fonctionner parfaitement. Au bout du compte on disposera même d'un véritable affichage de type oscilloscope réutilisable avec des courbes différentes.

Mais s'agissant juste d'un affichage, d'un effet visuel, n'est-ce pas un peu lourd ?

On peut en effet réaliser la chose bien plus rapidement et sans avoir de debug à faire (puisque pas de code) si on "pense" design, pur dessin. Et au lieu d'ouvrir Visual Studio, ouvrons Blend...

Bien plus que l'exemple lui-même (assez simple) c'est cette démarche sur laquelle je souhaite attirer votre attention.

Côté réalisation comment cela se passe alors ? Deux contraintes nous sont données : animer un spot selon une courbe "réaliste" et avoir un rendu de la rémanence de l'écran. Prenons le déplacement pour commencer: il suffit de positionner un cercle à gauche de l'écran de l'oscilloscope (une image réalisée sous Design), d'ouvrir un StoryBoard et de créer une keyframe à 2 secondes du début puis de déplacer le cercle jusqu'à le faire sortir de l'écran. Le mouvement horizontal est réglé. Concernant le mouvement vertical et la simulation d'une courbe amortie, l'astuce consiste à déplacer en début d'animation le spot sur l'axe Y pour le faire monter. C'est tout, l'effet de courbe sera rendu en utilisant astucieusement les nouveaux modes d'Ease in et out de Silverlight /Blend. Le point de déplacement Y sera doté d'un Quintic In pour amortir le mouvement, le dernier point de l'animation sera lui doté d'un Elastic Out avec 5 oscillations et un Spingness de -2. On peut bien entendu ajuster ces éléments à sa guise.

L'effet du déplacement est ainsi rendu sans développement. Bien entendu il s'agit d'un simple affichage qui n'effectue pas le rendu d'une "vraie" courbe passée en paramètre. Mais ce n'était pas ce qu'on voulait. Juste un effet visuel "réaliste" en y passant le moins de temps possible.

Pour l'écho visuel ? La ruse est un peu grosse mais elle passe plutôt pas mal : il suffit de copier le spot et son animation deux fois puis de décaler légèrement les timelines vers la droite. On change ensuite l'opacité du 1er et du 2d écho (de façon décroissante). L'effet est ainsi rendu sans aucun code.

Un oscilloscope plus vrai que nature ! Il ne lui manque que la parole pourrait-on dire. Justement, ajoutons un son pour "faire plus vrai" (disons plutôt "plus cinéma" car les vrais oscillos ne font pas de bip). Au départ on avait envisagé de lancer l'animation du spot en mode bouclage infini. Hélas les storyboards n'ont qu'un seul événement : Completed qui ne sera jamais déclenché en plus (puisque bouclage infini). Comment attraper le début de l'animation pour jouer le son et le synchroniser avec l'affichage du sport ?

Pas facile, et pas vraiment de solution pour l'instant. Mais il n'est pas interdit de faire marcher ses neurones ! Nous allons simplement supprimer le mode infini à la boucle d'animation du spot. Ensuite nous allons pouvoir gérer l'événement Completed de celle-ci. Dans le gestionnaire il suffira alors de relancer l'animation, on en profitera alors pour activer le son qui est placé dans un MediaElement (donc Stop() puis Play()).

Le son est maintenant synchronisé avec l'affichage.

Mais si le son devait être déclenché au milieu de l'animation ? Hmm.. Là nous serions un peu coincé je pense. Tout de suite le développeur dirait "ha je l'avais bien dit, si j'avais fait une gestion de sprite j'aurais presque fini!". Peut-être. Mais revenir à une animation pas à pas pour avoir la main n'est toujours pas ce qu'on veut.

Il est dommage que Silverlight ne puisse pas animer le play et le stop d'un MediaElement, c'est un point faible car un comportement largement utilisé sous Flash par exemple. Mais si tel était notre besoin, là encore, il serait possible de réfléchir un peu au lieu de repartir sur une animation pas à pas complexe.

Par exemple nous pourrions créer un petit UserControl possédant une propriété PlaySound (une propriété de dépendance bien entendu sinon elle ne sera pas animable) pouvant passer de 0 à 1 pour bénéficier de l'animation des Doubles sachant que seul le passage à la valeur exacte 1.0 déclenchera la séquence Stop/Play du MediaElement intégré. Dès lors ne resterait plus qu'à poser ce contrôle sur notre application et d'animer sa propriété PlaySound en la faisant passer de 0 à 1.0...

Voilà un bon exercice sur lequel je vous laisse vous amuser un peu. La solution dans un prochain billet !

Pour le reste de l'exemple il n'y a rien de bien compliqué. La petite fiche des settings est dessinée sous Expression Design, son titrage a été converti en Paths poru éviter d'avoir à intégrer la fonte à l'application Silverlight. Les sliders sont reliés aux propriétés qu'ils modifient par la nouvelle possibilité de binding élément à élément de Silverlight 3.

Pour le petit composant permettant de synchroniser le son dans une animation, je ne dirais qu'une chose : Stay Tuned !

J'allais oublier, le code du projet : MovingSpot.zip (91,58 kb)

La mer, les propriétés de dépendance et les user control's...

La mer... un souvenir qui va s'effacer jusqu'à l'année prochaine... Mais pour prolonger le plaisir nous avons Silverlight !

Et comment mieux rendre grâce à la Grande Bleue qu'en fabriquant un User Control la mettant en scène ? Et bien c'est ce que nous allons faire, ce qui permettra ludiquement d'aborder un problème épineux concernant les propriétés de dépendance sous Silverlight. Mais d'abord le visuel :

[silverlight:source=/SLSamples/LaMer/LaMer.xap;width=480;height=480]

La mer bleue, ou la mer rouge, au choix... et c'est bien ce choix qui va poser problème.

La base du UserControl 

Concernant le composant lui-même je suis parti d'un UserControl vide créé pour l'occasion. A l'intérieur un Path dessiné avec l'outil plume. Ce path est dupliqué deux fois (ce qui donne donc 3 exemplaires au final). Les deux copies sont modifiées : l'une tassée en largeur, l'autre agrandie sur le même axe.

Un StoryBoard complète le tout : les trois vagues sont déplacées de gauche à droite, l'animation est mise en mode Auto-Reverse et boucle infinie. Pour un mouvement plus doux en début et fin j'ai ajouté un ease in/out choisi parmi les nouveaux modes offerts par SL 3.

Le tout est englobé dans une grid pour bénéficier du clipping, le layout root étant une ViewBox, mais cette partie là de la cuisine interne du composant n'est pas forcément la plus subtile. Pour terminer j'ajoute un rectangle sans bordure qui est placé en mode Stretch au fond du Z-Order, il servira a définir un éventuel background.

Jusqu'à là rien de bien compliqué, juste un peu d'imagination est suffisant.

Là où ça se complique c'est lorsqu'il faut gérer la couleur des vagues et celle du rectangle de fond...

Héritage et propriété de dépendance sous Silverlight

Pour terminer correctement le UserControl il faut en effet penser à son utilisation. C'est à dire à ajouter des propriétés qui permettront à l'utilisateur du contrôle (le développeur ou l'intégrateur sous Blend) de modifier ses caractéristiques sans avoir besoin, bien entendu, de bricoler le source du contrôle lui-même.

Ici, nous souhaitons pouvoir modifier la couleur des vagues et celle du fond. Parfait me direz-vous, cela tombe bien, un UserControl descend de Control qui lui-même définit deux propriétés on ne peut plus à propos : Foreground et Background. Il "suffit" de les surcharger.

En effet, "il suffit de". Yaka.

Première chose, ces propriétés sont dites de dépendance (dependency property). Voir à ce sujet mon article Les propriétés de dépendance et les propriétés jointes sous WPF (article à télécharger). Ces propriétés ne sont pas définies comme ce qu'on nomme aujourd'hui pour les différencier les "propriétés CLR", les propriétés habituelles. Je vous renvoie à l'article cité ici pour creuser la question si vous ne connaissez pas les propriétés de dépendance.

Dès lors, et telles que fonctionnent ces propriétés spécifiques de Silverlight et WPF, pour les surcharger il faut passer par un système de métadonnées autorisant l'affectation d'une méthode callback. Dans cette dernière il est facile de répercuter sur le visuel les changements de valeur de la propriété. L'override d'une propriété de dépendance est donc assez simple. Sous WPF. Et c'est là qu'est le problème.

En effet, tout à l'air tellement merveilleux sous Silverlight depuis la version 2 qui accèpte du code C#, qu'on en oublie que si tout le Framework .NET pouvait tenir dans quelques méga octets on se demanderait bien pourquoi l'installation du dit Framework pour une application classique (desktop) réclame des dizaines et des dizaines de méga octets... Y'a un truc. Y'a même une grosse astuce je dirais : forcément yapatou. En clair, le Framework Silverlight est un découpage chirurgical de haute précision pour donner l'impression que tout fonctionne tout en évitant 90% du code du Framework. Et il y a des petits bouts qui manquent, et parfois des gros !

Concernant les propriétés de dépendance, l'équipe de Silverlight a implémenté le principal mais a laissé de côté les subtilités. Les métadonnées sont par exemple moins sophistiquées. Mais il n'y a pas que les données qui ont été simplifiées, les méthodes aussi. Et de fait, en tout cas pour l'instant, il manque aux propriétés de dépendance Silverlight la possibilité de les surcharger.

Aie ! Comment réutiliser Foreground et Background définies dans Control et accessibles dans le UserControl s'il n'est pas possible de modifier les métadonnées et d'enregistrer notre propre callback ? J'ai longuement cherché car le problème est loin d'être évident à résoudre. Certains préconisent même face à ce problème de redéfinir vos propres propriétés. C'est tellement horrible comme solution que je m'y suis refusé. Comment avoir le courrage de définir une couleur de fond et une couleur d'avant plan au sein d'un composant visuel qui affichera fièrement de toute façon Foreground et Background qui n'auront, hélas, aucun effet ? Quant à faire une réintroduction de ces propriétés (avec le mot clé "new"), n'y pensez pas, j'ai essayé et ça coince un peu (par code ça marche, mais le XAML se fiche de la redéfinition et utilise toujours la propriété originale, ce n'est pas un bug mais une feature ou plutôt un effet assumé du fameux découpage savant dans le Framework).

J'avoue que pour l'instant cet oubli volontaire dans Silverlight me chagrine. Pourquoi l'équipe Silverlight, qui fait la chasse au gaspi un peu partout, s'est amusée à définir ces deux propriétés dans la classe Control si on ne peut pas en hériter, sachant que Control ne sert à rien d'autre qu'à créer des classes héritées  ? C'est assez mystèrieux même si je suppose qu'il s'agit d'un problème de compatibilité avec WPF, Silverlight en faisant moins que son grand frère mais toujours en permettant que cela soit transparent pour le code. Bref, à satisfaire deux besoins opposés, d'un côté en coder le moins possible pour assurer la taille la plus petite au plugin et de l'autre assurer la compatibilité du code avec WPF, on finit par tomber sur des paradoxes de ce genre.

L'Element binding (ajouté dans SL 3) n'est pas utilisable non plus, à moins de donner un nom au UserControl (je veux dire à l'intérieur même de la définition de celui-ci). Ce qui n'est pas acceptable car si l'utilisateur du composant change ce dernier, le binding est cassé. Pire si l'utilisateur tente de placer deux instances sur une fiche, il y aura un conflit de nom. Solution inacceptable donc. J'ai testé, je pense, toutes les combines possibles. Mais j'ai enfin trouvé celle qui fonctionne !

La Solution

La piste de l'Element binding, nouvelle feature de SL 3, n'était pas mauvaise. Le problème c'est qu'en Xaml cela réclamait de pouvoir indiquer le nom de la source (le UserControl) alors même qu'à l'intérieur de la définition de notre contrôle il n'était pas question de lui donner un x:Name figé.

Mais en revanche, ce qui est possible en Xaml l'est tout autant par code (et souvent inversement d'ailleurs). Par chance, la classe permettant de définir un Binding n'utilise pas les noms pour la source ni le destinataire. Elle utilise les noms des propriétés mais là on les connait et ils ne changeront pas. Du coup, en définissant le Binding dans le constructeur (ou plutôt dans le gestionnaire de l'événement Loaded) on peut référencer "this", c'est à dire l'instance du UserControl, sans connaître son nom. On peut donc créer un lien élément à élément entre la propriété Fill des Path's et la propriété Foreground du UserControl (idem pour le Fill du rectangle et la propriété Background du UserControl).

Et ça marche ! Lorsqu'on compile tout ça et qu'on pose un composant "LaMer" sur une fiche on peut modifier la propriété Foreground et les vagues changent de couleur.

 

   1:          public LaMer()
   2:          {
   3:              // Required to initialize variables
   4:              InitializeComponent();
   5:              Loaded += new System.Windows.RoutedEventHandler(LaMer_Loaded);
   6:          }
   7:   
   8:   
   9:          private void LaMer_Loaded(object sender, System.Windows.RoutedEventArgs e)
  10:          {
  11:              // l'astuce est là !
  12:              var b = new Binding("Foreground") { Source = this, Mode = BindingMode.OneWay };
  13:              Vague1.SetBinding(Shape.FillProperty, b);
  14:              Vague2.SetBinding(Shape.FillProperty, b);
  15:              Vague3.SetBinding(Shape.FillProperty, b);
  16:   
  17:              var bb = new Binding("Background") { Source = this, Mode = BindingMode.OneWay };
  18:              rectBackground.SetBinding(Shape.FillProperty, bb);
  19:             
  20:              VaguesAnim.Begin();
  21:          }

Pour le Background on fait pareil avec le rectangle. Mais, allez-vous me dire (si si, vous y auriez pensé, un jour :-) ), pourquoi aller mettre un rectangle pour obtenir une couleur de fond alors même qu'il y a déjà une grille en dessous ? La grille possède aussi une propriété Background. Pourquoi, hein ?

La réponse est simple, j'ai forcément essayé, et ça fait un magnifique plantage avec une erreur dont le message fait peur en plus (du genre "anomalie irrémédiable dans cinq secondes tout va sauter, non ça a déjà sauté!"). Le message d'erreur étant assez peu clair quant aux raisons du plantage j'ai fini par abandonner. Ce qui marche une ligne avant pour la propriété Fill des rectangle avec la propriété Foreground du UserControl ne fonctionne pas du tout pour le Background de la grille liée au Background du UserControl. Là, ce n'est pas une feature, je penche sérieusement pour un gros bug.

Cela étant donné, j'ai donc ajouté un rectangle en fond pour qu'il puisse justement servir de ... Background. Et là ça passe. Ouf !

Ouf !

Silverlight c'est génial, c'est tout .NET et tout WPF dans un petit plugin. Mais dès qu'on sort du carré de verdure, on tombe dans les bois et là des loups il y en a quelques uns qui vous attendent au tournant ! Cela est logique, on s'y attend, c'est le prix à payer pour avoir .NET dans un browser Internet. Forcément le costume est un peu serré, le tissu a été économisé. Mais cela ne change rien à l'amour qu'on porte à Silverlight, au contraire, on se rend compte ainsi à quel point le travail de l'équipe Silverlight a été (et est encore) un véritable casse-tête et à quel point ils ont réussi un tour de force en faisant entrer un éléphant dans une boîte d'allumettes... Tout de même, une fois arrivé à la solution j'ai poussé un grand Ouf!

Le code source du projet : LaMer.zip (62,54 kb)

Pour de nouvelles aventures : Stay Tuned !

Bien commencer avec Silverlight 3 (les setup indispensables)

Silverlight 3 est disponible depuis juillet dernier mais il n'est pas toujours évident de savoir exactement ce qu'il est nécessaire de télécharger et où pour bien commencer.

Les vacances sont passées par dessus tout ça, du coup vous n'avez pas forcément bien suivi l'action, alors pour vous voici en mode slow motion, le replay des indispensables à installer pour s'amuser comme un fou avec Silverlight 3 et surtout être au point pour concevoir des applications RIA !

Silverlight 3

Le runtime est indispensable pour, au minimum, pouvoir exécuter les applications : Le runtime Silverlight 3
Le SDK est en revanche le minimum syndical pour concevoir des applications Silverlight 3 : Le SDK Silverlight 3

Expression Blend 3 avec Sketchflow

Blend est un l'outil indispensable pour créer des applications WPF et Silverlight. D'autant plus que depuis la version 3 de Silverlight il n'y a plus d'affichage du visuel sous VS 2008. Il reste la possibilité de tout faire à la main en mode éditeur de XAML mais franchement ce n'est pas comme ça qu'on peut créer sérieusement un visuel alors : Blend 3 et Sketchflow
Vous noterez qu'il s'agit d'une version d'essai, Blend 3 n'a pas de version gratuite.

Visual Studio 2008

En attendant la version 2010 qui intègrera le designer visuel de Silverlight, il est toujours nécessaire de posséder VS 2008. En effet, Blend ne gère pas le debug qui ne peut s'effectuer que par VS. Bien qu'un éditeur de code a été ajouté à Blend 3 il est aussi plus confortable d'utiliser Visual Studio pour toute la partie code. Si vous ne possédez pas VS 2008 : Version d'essai de VS 2008

Visual Web Developer Express 

Si vous préférez un outil gratuit, n'oubliez pas que Visual Web Express est une bonne alternative à Visual Studio.

Outils Silverlight

Tout un tas de choses indispensables notamment pour permettre de travailler sous Visual Studio ou Visual Web Express. A noter que VS doit être absolument patché avec le SP1 avant d'installer tout ça : Les Silverlight Tools.

Le Toolkit

Silverlight 3 est un produit riche, mais il l'est encore plus lorsque le toolkit est installé. De très nombreux contrôles indispensables sont ajoutés : le Silverlight Toolkit.

Les RIA Services

Grâces aux Services RIA, la gestion de données distantes (n-tiers) devient un jeu d'enfant. C'est un must pour tous ceux qui souhaitent développer des applications orientées données avec Silverlight : .NET RIA Services.

Deep Zoom Composer

Si vous souhaitez créer des images Deep Zoom, il est nécessaire de posséder le Composer : Deep Zoom Composer.

Le site officiel

Encore un lien indispensable ! La partie "getting started" vous renseigne sur les téléchargement de base et propose même un installeur global "Microsoft Web Platform". Vous trouverez aussi sur ce site de nombreuses vidéos de présentation et de formation (en anglais) : Le Site officiel Silverlight.

Dot.Blog

Et oui ! Si vous souhaitez rester à jour et être au courant, le mieux c'est encore de venir sur Dot.Blog le plus souvent possible ou mieux, de s'abonner au flux RSS et surtout : Stay Tuned !

 

Silverlight 3 : Un média player complet bien caché !

Silverlight 3 est livré de base avec un contrôle très versatile, MediaElement, capable de jouer de nombreux formats comme les mp3 ou les vidéos.

Si ce contrôle est très puissant il est un peu "nu" de base et il faut soi-même ajouter les boutons de commande comme "play" et looker l'ensemble. Avec Expression Blend 3 c'est un jeu d'enfant. Enfin, de grand enfant qui a un peu de temps devant lui tout de même. D'où la question : n'existerait-il pas un MediaElement déjà tout habillé ?

Si vous possédez Expression Media Encoder 3 vous savez que cet extraordinaire outil (servant principalement à encoder des médias) donne le choix entre plusieurs formats de sortie dont des projets HTML tout fait intégrant un média player tout looké, et mieux encore, avec le choix parmi de nombreux modèles.

Quel rapport entre Media Encoder et un projet Blend/VS ? C'est tout simple : lorsque vous installez Media Encoder, sous Blend 3 dans l'onglet Assets vous disposez, en plus de MediaElement d'un nouveau contrôle "MediaPlayer" !

Par défaut ce composant ressemble à l'image ci-dessous. Pour le relooker, il suffit de faire un clic droit et d'éditer un copie du template !

Reste la question à 10 centimes d'euro : oui mais Media Encoder est livré avec de nombreux modèles dont certains ont déjà un look sympa, ne pourrait-on pas récupérer ces modèles au lieu de templater à la main le MediaPlayer ?

Si, c'est possible (© Les Nuls, "Hassan Cehef").

Comment ? Là c'est plus cher... Non, comme je suis un chic type, voici la solution gratuite :

Encoder 3 s'installe avec le code source des modèles qui se trouvent dans le répertoire "C:\Program Files\Microsoft Expression\Encoder 3\Templates\en", il suffit donc de piocher le modèle qu'on désire utiliser, et grâce aux sources d'extraire le contrôle avec son template et de l'intégrer à son propre projet !

Le célèbre Tim Heuer décrit (en englais) la méthode à suivre, je vous renvoie ainsi à son billet Using Encoder Templates in your Silverlight Application si jamais vous n'arrivez pas à vous dépatouiller seul avec le code source des projets Encoder 3.

Intégrer de la vidéo, même HD, dans une application Silverlight n'a jamais été aussi simple... et beau.

Amusez-vous bien, et .. Stay Tuned !