Dot.Blog

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

« Il neige » ou les boucles d’animation, les fps et les sprites sous Silverlight

Il n’est jamais trop tard pour préparer Noël et la période estivale est le meilleur moment pour parler de neige, vision rafraîchissante s’il en est. Mais tel n’est pas réellement mon propos. Sous ce prétexte glacé (mais pas glaçant) se cache des notions techniques très utiles sous Silverlight (ou WPF).


Les boucles d’animation, le contrôle des fps (frame per second, images par seconde) et les sprites (petits objets autonomes animés) sont certes autant d’incursions dans le monde du jeu mais la créativité permet d’envisager mille utilisations de ces techniques dans d’autres contextes.
Je me suis librement inspiré d’un billet s’intitulant « Making it snow in Silverlight » écrit par Mike … Snow (facile à se souvenir !). J’utilise un code différent et (à mon sens) amélioré, mais il était important de rendre à Snow la neige qui lui appartient (et de faire un peu d’humour bas de gamme au passage).


Avant d’expliquer certains aspects, voici une démonstration live que vous pouvez passer en mode plein écran :

[silverlight:source=/SLSamples/IlNeige/IlNeige.xap;width=500;height=350]

Le but

Le but du jeu est donc de faire tomber de la neige sur un fond, c'est-à-dire de faire se mouvoir des centaines d’objets autonomes ayant chacun un aspect légèrement différent et une trajectoire un peu aléatoire. Le tout assez vite pour que cela paraisse naturel.

Le principe

Chaque flocon de neige est en réalité une instance d’un UserControl, il y a ainsi des centaines d’instances au même instant. Chaque flocon est responsable de son affichage et de la façon dont il avance à chaque « pas ». La coordination générale étant réalisée par une boucle d’animation principale, c’est elle qui scande le rythme et demande à chaque flocon d’avancer d’un « pas ».
Tout se trouve donc dans deux endroits stratégiques : la boucle principale et le UserControl « Flocon ».

Suivre l'article : le code n'étant pas très lisible dans le blog, sauf extraits très courts, je n'ai volontairement pas placé ce dernier dans le corps du billet. Je vous conseille ainsi de télécharger le projet (en fin de billet) et de suivre les explications en ayant le code chargé sous Blend 3 ou Visual Studio (2008 + SL3 minimum).

Le sprite Flocon

Il s’agit d’un UserControl possédant une interface très simple puisqu’il s’agit d’un bitmap en forme d’étoile (ou pourrait utiliser n’importe quelle forme vectorielle créée sous Design ou Illustrator). Côté XAML on retrouve ainsi une grille avec un PNG à l’intérieur, rien de palpitant. A noter qu’au niveau du UserControl lui-même on définit deux transformations : une d’échelle (FlakeScale) et une de rotation (FlakeRotation). Dans l’exemple de la neige cette dernière n’a que peu d’effet visuel sauf lorsqu’on choisit une taille assez grosse pour les flocons. L’œil ne discerne pas forcément la rotation de façon consciente mais comme elle est choisit aléatoirement pour chaque flocon cela renforce l’aspect global plus « organique ». De plus il s’agit d’illustrer des techniques utilisables dans d’autres contextes ou avec d’autres sprites pour lesquels l’effet serait plus évident. La technique est extrapolable à toutes les transformations supportées, même les nouvelles transformations 2,5D de Silverlight 3.
Le code de la classe Flocon se divise en deux points : le constructeur et la méthode « Tombe ».

Le constructeur

Il est responsable de la création de chaque instance. C’est à ce niveau qu’on introduit un peu d’aléatoire dans la rotation du flocon. On choisit aussi à ce moment la position de départ en X (la position en Y étant toujours à zéro au départ).
La gestion des flocons est séquentielle (par la boucle d’animation que nous verrons plus loin). De fait il n’y a pas de multithreading et on peut se permettre un développement très simple de la classe. Par exemple les limites du tableau de jeu sont stockées dans des propriétés statiques de la classe. Le programme initialise ces valeurs au lancement et les modifie à chaque fois que la taille du tableau est changée (redimensionnement du browser). 
Le constructeur fixe aussi de façon aléatoire l’échelle de l’instance afin que chaque flocon soit de taille légèrement différente par rapport à une échelle de base. Cela permet d’obtenir un effet de profondeur de champ.

La méthode « Tombe »

Il s’agit bien de tomber et non d’outre-tombe et de zombies façon Thriller du très médiatisé Jackson. Pour chaque flocon il s’agit donc ici de savoir choir, avec si possible le sens du rythme et du mouvement du sus-cité Michael.
Pour ce faire la méthode « Tombe » doit calculer la nouvelle position X,Y du flocon. Les flocons évoluent dans un espace de type Canvas particulièrement indiqué pour tout ce qui doit être positionné de cette façon à l’écran.
Sur l’axe des Y le déplacement s’effectue d’un pixel à la fois. Une incrémentation simple est utilisée. Sur l’axe horizontale le flocon change régulièrement de direction pour rendre l’ensemble visuel plus réaliste. On utilise ici un tirage aléatoire pour savoir s’il faut ou non changer de direction. Dans l’affirmative on modifie la valeur du pas de déplacement (hzDeplacement). La position sur X est alors calculée en ajoutant cette valeur à la position courante.
Reste à savoir quand un flocon n’a plus d’intérêt, c'est-à-dire lorsqu’il échappe au champ de vision et qu’il peut être détruit (n’oublions pas que des centaines d’instances sont gérées à la fois et que tout flocon inutile doit être détruit le plus vite possible pour d’évidentes raison d’efficacité). C’est le rôle du test en fin de méthode qui initialise la valeur « HorsZone ». Lorsqu’elle passe à true, le flocon peut être détruit. Cette information est utilisée par la boucle principale.


La boucle d’animation

Maintenant que nous disposons d’un modèle de flocon (la classe Flocon) reste à animer de nombreuses instances pour donner l’illusion de la neige qui tombe.
La technique pourrait utiliser un Timer ou bien une StoryBoard vide dont le déclenchement est réalisé à chaque pas (et comme elle s’arrête immédiatement on obtient une sorte de Timer rythmant la boucle principale). Cette dernière façon de faire était très utilisée sous Silverlight 1.x.  Selon la vitesse de la machine cliente la boucle est ainsi déclenchée plus ou moins vite ce qui évite le problème de fixer un pas déterminé comme avec un Timer. Toutefois il est en réalité inutile de calculer la position des flocons plus rapidement que les FPS, pure perte d’énergie, ni de calculer moins souvent (autant abaisser les FPS).


Sous Silverlight 2 un événement a été ajouté donnant accès au rythme du moteur de rendu : CompositionTarget.Rendering. En gérant la boucle principale dans ce dernier on s’assure d’être parfaitement dans le tempo du moteur de rendu, ni plus rapide, ni plus lent.

Cette technique a toutefois dans le principe un défaut connu dans tous les jeux programmés de la sorte : la célérité de l’ensemble dépend de la vitesse de la machine hôte. Un PC très lent fera donc tomber la neige trop lentement, alors qu’un PC fulgurant des années 2020 ne permettra même plus de distinguer le déplacement des flocons tellement cela sera rapide, rendant le jeu « injouable ». Pour le premier problème il n’y a pas vraiment de solution, on ne peut donner plus puissance à la machine hôte (sauf à permettre à l’utilisateur de diminuer la charge du jeu lui-même en limitant par exemple le nombre maximum de flocons, le paramètre de densité peut jouer ce rôle dans notre exemple). Quant au second problème il trouve sa réponde dans la limite à 60 FPS par défaut de Silverlight, limite modifiable via les paramètres du plugin (voir ce billet).

Débarrassés des écueils propres à cette technique, l’événement de rendering devient l’endroit privilégié pour placer le code d’une boucle principale d’animation.

Le contrôle des FPS

Lorsque vous exécutez une application de ce type il est important de contrôler les FPS pour voir comment certaines améliorations du code peuvent accélérer ou non le rendu. Pour ce faire Silverlight permet d’activer l’affichage de deux informations essentielles dans la barre d’état du browser : les FPS actuels et la limite fixée dans le plugin. L’activation se fait via Application.Current.Host.Settings en plaçant EnableFrameRateCounter à true. Il est bien entendu vivement conseillé de déconnecter la fonction en mode Release. Si votre navigateur et ses réglages le supporte vous devez en ce moment pouvoir lire ces informations en bas à gauche de la barre d'état (et si l'exemple live de l'application en début de billet s'est chargé correctement).

Attention : Selon les réglages de sécurité Internet Explorer autant que FirexFox peuvent ne pas autoriser la modification de la barre d’état. Sous IE (sous FF cela est quasi identique) il faut vérifier le paramètre Outils / Options Internet / Sécurité / Zone Internet / Personnaliser le niveau / Autoriser les mises à jour à la barre d’état via le script. Ce paramètre doit être activé pour voir les FPS.

Le Rendering

A chaque pas du rendering nous allons réaliser deux tâches essentielles : déplacer les flocons déjà présents et supprimer ceux devenus inutiles et créer de nouveaux flocons.
Pour déplacer les flocons nous ne faisons que balayer la liste de tous les flocons et appeler la méthode « Tombe() » de chacun. Au passage nous archivons dans une liste temporaire tous les flocons dont l’état HorsZone est passé à true. En fin d’animation les flocons inutiles sont supprimés de la liste principale les laissant ainsi disponible au Garbage Collector.

Pour créer de nouveaux flocons nous déterminons si la densité désirée est atteinte ou non. Le processus est randomisé pour plus de réalisme bien que le seuil de densité soit lui fixé par le programme (et modifiable dans notre exemple). Comme le placement de chaque flocon est réalisé dans le constructeur de la classe Flocon il n’y a rien d’autre à faire que de créer les flocons et de les ajouter à la collection Children du Canvas parent.

Le plein écran

Silverlight permet de passer une application en mode plein écran. Sachez toutefois que ce mode pose de sérieuses conditions à son utilisation. Notamment il ne peut être activé qu’en réponse directe à un clic de l’utilisateur. Cela permet d’éviter (en partie) qu’une application Silverlight soit utilisée de façon à tromper l’utilisateur final en prenant le contrôle de tout l’écran sans son autorisation (on pourrait penser à un fake du bureau Windows permettant par exemple d’extorquer un login). De même, lorsque l’application bascule en mode plein écran un message impossible à supprimer est affiché pour avertir l’utilisateur et lui indiquer que l’appui sur Escape permet de sortir de ce mode spécial.

Si cela n’était pas suffisant, les saisies clavier sont impossibles en plein écran. Seules certaines touches ont un effet (voir la documentation sur MSDN). Ne pensez pas utiliser le plein écran pour une application de saisie par exemple. En revanche pour visionner des vidéos ou pour créer un jeu les touches disponibles et la gestion de la souris sont suffisants. C'est le cas de notre démo.

Une fois connues ces limites raisonnables (Microsoft ne veut pas, à juste titre, que Silverlight soit utilisé par des fraudeurs et autres adaptes du phishing) le passage en plein écran s’effectue par Application.Current.Host.Content.IsFullScreen en mettant cette propriété à true, et ce en réponse au clic sur un bouton en général.

Le code

Télécharger le projet complet de l'exemple "IlNeige" : IlNeige.zip (229,54 kb)

Silverlight : Dessiner des horloges, un peu de trigonométrie !

Tout le monde sait faire un cercle, enfin une Ellipse particulière sous Silverlight ayant une hauteur et une largeur identiques. En revanche placer les marques des minutes ou placer correctement le texte des heures dans le cadran n'est pas toujours évident.

Si on tente de le faire "à la main", et même sous Blend, c'est une véritable galère (imaginez 60 petites ellipses pour les secondes à répartir soigneusement, sans parler d'un éventuel changement de dimension qui mettrait tout par terre !).

La seule solution raisonnable passe ici forcément par code. Mais malgré la croyance populaire tous les informaticiens ne sont pas forcément des "bêtes" en math ! On peut être un expert en SQL sans se rappeler ses cours de trigonométrie et on peut développer toute une architecture multithreadée sans même savoir ce qu'est un Groupe de Lie. Mais parfois ce genre de connaissance peut manquer notamment dès qu'on aborde le traitement d'image ou de dessin.

[silverlight:source=/SLSamples/ClockHours/ClockHours.xap;width=360;height=222]

Le but du jeu est donc ici de répartir harmonieusement des objets sur un cercle. Dans un cas il s'agit de texte (les heures) dans l'autre des cercles (les dots). Mais avant de regarder le code, rappelons quelques points mathématiques :

L'unité Math fonctionne en radians c'est à dire qu'un cercle vaut 2 Pi (donc deux fois 3.1415926...). C'est bête parce qu'en général un humain normalement consituté raisonne plutôt en degrés, ce qui est plus facile à se représenter mentalement.

 

 

Comme on peut le remarquer en radian le 0 se trouve au milieu à droite du cercle et non pas en haut à midi comme on pourrait s'y attendre. Du coup, comme les fonctions mathématiques de .NET marchent en radians si on raisonne en degrés il faudra penser à cette petite spécifité lors de la conversion. Par exemple, la place de "1 heure" sur une horloge se situe à 300 degrés et non pas à 30° (1 pas de 360 divisé par 12).

Le code ci-dessous permet d'écrire les heures dans l'exemple live Silverlight plus haut dans ce billet :

private void writeHours(int radius, int offsetx, int offsety, Canvas parent, double fontsize, Color color)
        {
            var h = 1;
            for (var angle=300;angle<=630;angle+=30)
            {
                var t = new TextBlock {
                        Foreground=new SolidColorBrush(color), 
                        Text=h.ToString(),
                        FontSize = fontsize};
                h++;
                t.SetValue(Canvas.LeftProperty,(radius * Math.Cos(DegreetoRadian(angle))) + offsetx);
                t.SetValue(Canvas.TopProperty,(radius * Math.Sin(DegreetoRadian(angle))) + offsety);
                parent.Children.Add(t);
            }
        }

Les paramètres sont les suivants :

  • radius : rayon du cercle virtuel sur lequel les heures sont posées
  • offsetx et offsety : positionnement du centre du cercle dans le canvas parent, sachant que le 0,0 se trouve en haut à gauche.
  • parent : le Canvas parent
  • fontSize : taille de la fonte
  • color : la couleur de la fonte

Pour dessiner les dots on utilise le même principe (sans s'encombrer des détails du point de départ décalé puisque toutes les dots sont identiques).

Le code n'est pas optimal et on peut certainement faire mieux, mais cela vous donne un point de départ ... Pour compléter le tout, vous pouvez télécharger le projet : ClockHours.zip (61,42 kb)

Stay Tuned !

Silverlight 3 (+WPF) : Hatching Effect gratuit + Sources

Les pixel shaders de Silverlight 3 fournis "out of the box" ne sont que deux : le drop shadow et le blur. Mais comme cela était prévisible de nombreux programmeurs exercent leur talent en créant de nouveaux effets...

Forcément l'équipe Microsoft de la suite Expression est très bien placée pour ce genre d'exercice et elle nous propose via son blog un effet de crayonnage assez bien fait. Le tout avec install pour automatiquement voir l'effet dans les palettes de Blend 3 mais aussi avec le code source pour l'étudier et faire ses propres effets.

Le mieux est de vous rendre directement sur le blog de l'équipe Expression, et plus particulièrement sur le billet présentant le Hatching effect. Source et install sont téléchargeables depuis le billet.

A noter, l'effet fonctionne aussi avec WPF selon ce qui est dit mais je n'ai testé que sous Silverlight.

Stay Tuned !

Silverlight : Application transparente

Il est tout à fait possible de rendre une application (ou une fenêtre) Silverlight transparente, c'est à dire que son contenu apparaîtra sur la couleur de fond du site hôte. Cela peut s'avérer très utile pour des petits morceaux de Silverlight placés sur un site Html ou Asp.net, pour créer une barre de menu par exemple. Si la charte couleur du site est modifiée il ne sera pas nécessaire de modifier et recompiler la ou les applications Silverlight.

Pour arriver à ce résultat il faut faire deux choses simples dont la syntaxe dépend de la nature de la page où s'insère le plugin (ASP.NET ou HTML)

exemple ASP.NET :

<asp:Silverlight PluginBackground="Transparent" Windowless="true" ID="Menu" runat="server" 
Source="~/ClientBin/Menuxap" MinimumVersion="xxxxx"  />  

exemple HTML :

<object data="data:application/x-silverlight," type="application/x-silverlight-2-b2" > 
<param name="source" value="ClientBin/Menu.xap"/> 
<param name="onerror" value="onSilverlightError" /> 
<param name="pluginbackground" value="Transparent" /> 
<param name="windowless" value="true" />  
</object>   

Les deux paramètres importants étant : PluginBackgrounf et Windoless .

Par défaut une application Silverlight occupe 100% de la fenêtre hôte, dans le cas d'une barre de menu ou d'un autre type de fenêtre intégré à une page ASP.NET/HTML on ajoutera donc dans les balises la déclaration de la hauteur et de la largeur exacte qu'on souhaite réserver pour le plugin. Bien entendu le contenu de l'application Silverlight doit être dépourvu d'arrière plan, cela va sans dire, sinon, transparence ou non, c'est lui qui sera affiché et non le background de la page hôte...

 

Silverlight 3 : La multi-sélection

Parmi les petites évolutions de Silverlight 3 qui ne méritent pas un article de fond mais qu'il faut noter tellement elles simplifient les choses, j'apprécie le support de la multi sélection dans les ListBox.

La nouvelle propriété s'appelle sans malice : SelectionMode et elle peut prendre les valeurs suivantes : Single, Multiple, Extended. En mode Single on retrouve le comportement par défaut mono sélection. Les deux autres modes permettent d'accéder au comportement multi sélection. En mode Multiple la sélection s'opère par le clic sur un item, en enfonçant Ctrl ou Shift. Le mode Extended fait que le Shift permet de sélectionner des étendues.

Jouez avec notre ami Casimir dans l'exemple ci-dessous, et grâce à la nouvelle ListBox Silverlight 3 confectionnez votre propre Gloubiboulga en partant de la recette originale et de ses options (pour les plus gourmands !) :

[silverlight:source=/SLSamples/MultiSelect/MultiSelect.xap;width=411;height=203]

Bon appétit, et... Stay Tuned !

Silverlight 3 : Styles Cascadés (BasedOn Styles)

Toujours dans ma petite série sur les nouveautés de Silverlight 3 je vais vous présenter aujourd'hui une feature plaisante : les styles cascadés.

En soi rien de nouveau à l'ouest puisque c'est le principe même des feuilles de styles CSS (qui y puisent d'ailleurs leur nom). Mais le CSS s'applique à quelques éléments simples HTML alors que là nous parlons de styles Silverlight, c'est à dire d'objet complexes pouvant définir tout un visuel, animations comprises.

[silverlight:source=/SLSamples/BasedOnStyle/BasedOnStyle.xap;width=405;height=150]

Dans l'application Sivlerlight 3 ci-dessus (fonctionnelle, ce n'est pas une capture écran), vous voyez 4 boutons. Tous sont des boutons standard du framework.

  • Le premier, intitulé "Base SL" possède le style Silverlight par défaut
  • Le second, "Normal" est décoré par le style "BoutonNormal"
  • Le troisième "Gros" est décoré par le style "BoutonGros"
  • Et le quatrième "Alarme" est décoré par le style "BoutonGrosAlarme"

Visuellement c'est plutôt moche, je vous l'accorde, mais le but du jeu est de voir l'effet du cascading styling...

Le style "BoutonNormal" est défini comme suit :

<Style x:Key="BoutonNormal" TargetType="Button">
     <Setter Property="Width" Value="90" />
     <Setter Property="Height" Value="30" />
     <Setter Property="HorizontalAlignment" Value="Left" />
     <Setter Property="VerticalAlignment" Value="Bottom" />
     <Setter Property="BorderThickness" Value="2"/>
</Style>

Là où les choses deviennent plus intéressantes, c'est dans le style "BoutonGros" ci-dessous où l'on voit apparaître l'attribut BasedOn qui permet de fonder le style courant sur celui qu'on indique :

<Style x:Key="BoutonGros" 
         BasedOn="{StaticResource BoutonNormal}"
         TargetType="Button">
  <Setter Property="Width" Value="180" />
  <Setter Property="Height" Value="60" />
  <Setter Property="FontFamily" Value="Comic Sans MS"/>
</Style>

Enfin, le dernier style se fonde lui-même sur le précédent par le même mécanisme, le niveau de cascading n'étant pas limité. On peut voir notamment que le changement de famille de fonte introduit dans le style "BoutonGros" s'est propagé au style "BoutonGrosAlarme" (fonte Comic).

<Style x:Key="BoutonGrosAlarme" 
         BasedOn="{StaticResource BoutonGros}"
         TargetType="Button">
  <Setter Property="Width" Value="160" />
  <Setter Property="Height" Value="40" />
  <Setter Property="FontSize" Value="18"/>
  <Setter Property="FontWeight" Value="Bold"/>
  <Setter Property="Foreground" Value="Red"/>
  <Setter Property="BorderThickness" Value="4"/>
  <Setter Property="BorderBrush" Value="#FFFF0202"/>
</Style>

Voilà, c'est tout simple, mais cela peut radicalement simplifier la création de gros templates pour des applications. Tous les avantages du Cascading Style Sheet de HTML dont l'intérêt ne se démontre plus, mais appliqué à des objets et à la sophistication de Silverlight. Que du bonheur...

Bon Styling,

...Et Stay Tuned !

Silverlight 3 : L'Element Binding

L’Element Binding est une nouvelle feature de Silverlight 3 déjà présente sous WPF.

L’Element Binding définit la capacité de lier les propriétés des objets entre eux sans passer par du code intermédiaire. Cette possibilité existait déjà sous WPF, on la retrouve désormais sous SL3.

Pour simplifier prenons l’exemple d’un panneau d’information, par exemple un Border avec un texte à l’intérieur. Imaginons que l’utilisateur puisse régler l’opacité de cette fenêtre par le biais d’un Slider. (ci-dessous l'application exemple pour jouer en live).

[silverlight:source=/SLSamples/EBinding/EBinding.xap;width=452;height=240]

On place quelques éléments visuels sous le Border (ici des rectangles) afin de mieux voir l’effet du changement d’opacité.

Trois méthodes s'offre à nous pour régler le lien entre le Slider et l'opacité du Border. J'appellerai la première "méthode à l'ancienne", la seconde "méthode du certifié" et la troisième "méthode Silverlight 3". Les trois ont leur intérêt mais si vous êtes très pressé vous pouvez directement vous jeter sur la 3eme solution :-)

Méthode 1 dite « à l’ancienne »

Le développeur Win32 habitué aux MFC ou à des environnements comme Delphi aura comme réflexe immédiat d’aller chercher l’événement ValueChanged du Slider et de taper un code behind de ce type :

MonBorder.Opacity = MonSlider.Value ;

Ça a l’avantage d’être simple, efficace, de répondre (apparemment) au besoin et de recycler les vielles méthodes de travail sans avoir à se poser de questions…

Méthode 2 dite « du certifié »

Ici nous avons affaire à un spécialiste. Son truc c’est la techno, suivre les guide-lines et écrire un code qui suit tous les dogmes de l’instant est un plaisir intellectuel. Parfois ses solutions sont un peu complexes mais elles sont belles et à la pointe de la techno !

Conscient que la solution « à l’ancienne » a un petit problème (en dehors d’être trop simple pour être « belle », elle est one way, le slider modifie l'opacité du border mais l'inverse ne fonctionne pas) il va chercher une solution objet élégante répondant à l’ensemble des cas possibles couvrant ainsi le two way, considération technique trop technophile pour le développeur du cas précédent.

Ici forcément ça se complique. C’est techniquement et intellectuellement plus sexy que la méthode « à l’ancienne » mais cela réclame un effort de compréhension et de codage :

Il faut en fait créer un objet intermédiaire. Cet objet représente la valeur du Slider auquel il est lié lors de son instanciation. Quant à l’objet Border, sa propriété Opacity sera liée par Data Binding standard à l’objet valeur.

Voici le code de la classe de liaison :

public class ValueBinder : INotifyPropertyChanged
       {
             public event PropertyChangedEventHandler PropertyChanged;
             private Slider boundSlider;
             public ValueBinder(Slider origine)
             {
                    boundSlider = origine;
             }
 
             public double Value
             {
                    get { return boundSlider==null?0:boundSlider.Value; }
                    set {
                           if (PropertyChanged!=null)
                                  PropertyChanged(this,
                                    new PropertyChangedEventArgs("Value"));
                           boundSlider.Value = value;
                    }
             }
       }

Cette classe, ou plutôt l’une de ses instances, servira a représenter la valeur courante du Slider. Ce dernier est lié à l’instance lors de la création de cette dernière (voir le constructeur de la classe ValueBinder ci-dessus).

Comment utiliser cette classe ?

La première chose est qu’il faut en créer une instance, cela peut se faire dans le code XAML ou bien dans le code behind de la façon suivante (dans le constructeur de la page par exemple) :

var valueBinder = new ValueBinder(slider);

Maintenant il suffit dans le code XAML de lier les deux objets à la valeur de la classe intermédiaire, par Data Binding :

Côté Slider, le code est :

<Slider x:Name="slider"Value="{Binding Value, Mode=TwoWay}"/> 

Côté Border :

<Border x:Name="borderInfo"Opacity="{Binding Value, Mode=TwoWay}"> 

Ne reste plus qu’à rendre visible l’objet intermédiaire par exemple en en faisant la valeur courante du DataContext de l’objet LayoutRoot :

LayoutRoot.DataContext = valueBinder;

Et voilà ! Ne reste plus qu’à compiler et vous le plaisir de voir que l’opacité du Border change bien lorsque le Slider est déplacé.

La chaîne est la suivante : la modification de la position du Thumb entraîne dans le composant Slider la modification de la valeur de la propriété Value. Comme celle-ci est liée par Data Binding TwoWay à la propriété Value de l’objet intermédiaire cette propriété va se trouver modifiée dans le même temps. Comme le Setter de la propriété notifie le changement de valeur de la propriété (la classe implémente INotifyPropertyChanged) et comme la propriété Opacity du Border est elle aussi liée par Data Binding à la propriété Value de l’objet intermédiaire, le Border sera « prévenu » du changement de valeur et sa propriété Opacité sera immédiatement modifiée pour recevoir la valeur courante de Value de l’objet intermédiaire ! C’est pas fun tout ça ? (hmmm j’en vois un qui suit pas là bas au fond… !).

Vous allez me dire, TwoWay, on veut bien te croire mais on ne le voit pas là … Pour l’instant cela se comporte exactement comme la première solution, juste qu’il faut avoir un niveau de certifié pour comprendre…

C’est pas faux. C’est pourquoi je vais maintenant ajouter un petit bout de code pour faire varier la valeur de la propriété Opacity du Border. Le plus simple est de gérer la roulette de la souris dans le Border :

<Border x:Name="borderInfo"MouseWheel="Border_MouseWheel"> 

Et dans le code behind :

private void Border_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) 
             { 
                    borderInfo.Opacity += e.Delta/1000d; 
                    e.Handled = true;
             }

Il suffit maintenant de faire rouler la molette au dessus du Border pour en changer l’opacité. Le TwoWay ? Regardez bien : le curseur du Slider avance ou recule tout seul… Pour éviter que la page HTML contenant le plugin Silverlight ne se mette à scroller il faut bien entendu indiquer que l'événement est géré (Handled=true) ce qui est fait dans le code ci-dessus.

Cette solution est élégante, complète mais complexe. Elle reste l’approche à préconiser dans tous les cas du même type car, on le voit bien, si la première solution est simple, elle n’est pas complète. Complexifier par plaisir est un mauvais réflexe, mais ne pas voir qu’une solution est trop simpliste est aussi un défaut qu’il faut fuir !

Bref, sous Silverlight 2 la solution présentée ici est une bonne solution. Sous Silverlight 3 qui supporte le Binding direct entre éléments (comme WPF) nous allons pouvoir faire plaisir à la fois au développeur du premier cas et à celui du second :

Méthode 3 dite « Silverlight 3 »

Comment marier le reflexe (plutôt sain) du premier développeur de notre parabole de vouloir faire vite et simple avec l’exigence intellectuelle (tout aussi saine) du second qui implique de faire « complet » ?

Pour illustrer l’Element Binding de façon simple, prenons un Scrollbar en mode horizontal et un Slider en mode vertical.

Et regardons le code XAML de leur déclaration :

<ScrollBar x:Name="scrollA"Value="{Binding Value, ElementName=sliderB, Mode=TwoWay}"/> 
 
<Slider x:Name="sliderB"Value="{Binding Value, ElementName=scrollA, Mode=TwoWay}" /> 

Et c’est tout ce qu’il y a à faire pour obtenir une solution complète, élégante, sans code behind et très facile à mettre en œuvre ! Merci Silverlight 3 !

En début de billet vous pouvez jouer avec les deux dernières implémentations (je n’ai pas implémenté la méthode 1) .

Vous pouvez aussi télécharger le code du projet (Blend 3 ou VS2008 avec les extensions SL3) : elementBinding.zip (61,43 kb)

Pour d'autres nouvelles, Stay Tuned !

Devoir de vacances

J'ai pris quelques jours de vacances bien mérités. Mais comme un geek ne part jamais sans son portable, forcément ça a été studieux !

Vous avez pu lire mes deux derniers billets, "Xaml l'ami des artistes" qui présente Enaxos Art, une petite vitrine Silverlight de travaux graphiques, ou bien hier l'annonce de la sortie de Silverlight 3 final et de Blend 3 RC avec Sketchflow.

Hélas les vacances se terminent. Mais comme je suis un filou et que mardi est férié, le retour ne se fera que Mercredi. Encore 48h de bonheur et de farniente à goûter aux joies des effets de perspectives 3D de Silverlight 3 et des nouveautés de Swift 3D de Electric Rain dont la V6 vient de sortir et avec laquelle je m'amuse bien !

Très vite la rentrée va approcher et avec elle les bonnes résolutions qu'on ne tient jamais. C'est tous les ans la même chose, alors en m'y prenant presque deux mois avant j'espère bien trouver cette année des résolutions tellement vagues qu'il ne sera pas vraiment possible de dire que je ne les ai pas tenues ! "Puisque ces mystères nous dépassent, feignons d'en être les instigateurs" disait sagement Cocteau !

Pour rester dans la sagesse populaire je vous prépare quelques tutors sur toutes ces nouveautés car comme le dit cette fois-ci le vieux romain qui sommeille en chaque auteur : Qui scribit bis legit ! (celui qui écrit lit deux fois !).

Ave ! Et bronzez bien à l'ombre de vos portables (le soleil c'est dangereux pour la peau, l'eau des plages est pleine de colibacilles fécaux et les rues sont bondées de touristes, mieux vaut donc rester au frais à développer et à se former !).

Stay tuned !

XML/XAML pretty printer gratuit

Il arrive souvent que du code XML soit produit "au kilomètre" sans mise en forme particulière. Même si Internet Explorer sait afficher un tel fichier en le mettant en forme automatiquement, on souhaite parfois disposer d'une version formatée lisible par un humain.

ODPrettyXml, un utilitaire console très simple qui ne fait que ça... Il traite les fichiers XML, mais aussi du XAML sans souci. Toutefois vous remarquerez que ODPrettyXml travaille toujours sur un fichier de sortie différent de l'original, certaines transformations pourraient avoir des effets non souhaités. L'utilitaire est donc avant tout conçu comme un "pretty printer" dont la vocation est de rendre le document plus lisible pour un humain. Les fichiers produits, même s'ils restent fonctionnels, n'ont pas vocation a être utilisé en programmation.

Pour le mode d'emploi, tapez ODPrettyXml sous console, l'aide sera affichée. Le programme ne demandant aucune saisie, il est possible de l'utiliser dans des fichiers de commandes (ou des batchs).

La syntaxe la plus habituelle est "ODPrettyXml <nom du fichier>" qui fabriquera automatique un fichier de sortie de même nom se terminant par "pretty" suivi de l'extension du fichier original (par exemple: toto.xml donnera toto.pretty.xml).

Si vous tapez "ODPrettyXml ?" vous obtiendrez la liste de tous les encoders connus et utilisables avec leur code page. C'est le nom qu'il faut utiliser en 3eme paramètre de ODPrettyXml. Par exemple pour utiliser unicode il faut taper "ODPrettyXml <source> <sortie> utf-16". Quand un encodeur est spécificé, il faut aussi saisir le nom du fichier de sortie (2d paramètre).

Dernière remarque, ODPrettyXml ne fait qu'encoder le fichier et le mettre en forme avec des indentations, notamment il ne contrôle pas si l'encodage demandé est conforme à celui déclaré dans le fichier source. Un fichier indiquant qu'il est codé en UTF-8 peut être encodé en UTF-16, son entête indiquera toujours UTF-8, le fichier n'est pas modifié par ODPrettyXml.

Téléchargement : odPrettyXml.exe (34,00 kb)
(exécutable .NET 3.5, mode console)
(projet VS 2008 complet. Le fichier de signature électronique est absent vous devrez en créer un autre).
Amusez-vous bien !
Et Stay Tuned pour d'autres nouvelles (notamment un gros article à venir les Splash screen sous Silverlight !)
(PS: l'aide du logiciel a quelques coquilles, à vous de les trouver et les corriger :-) )

F# et le "paradoxe de la secte"

F#, fils (ou frère ?) de OCaml, langage intégré à la prochaine version de Visual Studio, fait son buzz ...

Comme toute nouveauté, ne pas connaître c'est risquer de passer pour un idiot à la machine à café (voire à la cantine ou au chinois du coin le midi, l'effet est le même, juste qu'il est plus facile de quitter la machine à café en prétextant un truc urgent à faire que de se lever de table sans avoir fini son rouleau printemps).

De fait ne pas s'informer est en réalité un double crime : d'abord professionnel (rester au courant est un minimum syndical quand on est payé pour faire un job), ensuite social (passer pour un hasbeen est lourd à porter).

Heureusement je veille au grain et grâce à DotBlog vous ne commettrez pas ce double impair !

F# est un langage issu des labos de Microsoft. S'il est très proche de OCaml (au départ totalement compatible) il s'en éloigne par plusieurs points, ne serait-ce qu'en raison de sa parfaite intégration à l'édifice .NET.

Tout le problème avec un nouveau langage c'est qu'il place l'informaticien dans une situation embarassante. Car on attend d'un informaticien qu'il parle d'informatique et qu'il connaisse les outils propres à son métier. Jusqu'à là rien d'anormal. Mais concernant un langage on se retrouve vite devant ce que j'appelle le "paradoxe de la secte" : Soit vous n'y appartenez pas et toutes vos critiques et réserves seront balayées par ses partisans au principe que n'y appartenant pas vous n'en connaissez rien et que votre avis est totalement subjectif, soit vous y appartenez et le bien que vous en direz, tout comme le mal éventuellement, sera considéré comme sujet à caution par tous ceux qui n'appartiennent pas à la dite secte ...

Mais cela pose tout de même un sacré problème : peut-on dire du bien ou du mal d'un nouveau langage de programmation sans avoir écrit un vrai logiciel avec, ou doit-on forcément être capable de réécrire Excel en un week end avec un langage pour pouvoir prétendre donner son avis sur ce dernier ?

J'aime les paradoxes pour le jeu. Mais je n'aime pas l'inconfort intellectuel du "paradoxe de la secte" surtout appliqué à mon métier qui est en quelque sorte une apologie à la Logique et où le monde se règle à coups de If Then Else. Des If avec un Then mais sans Else c'est de la programmation risquée. Et d'ailleurs pour revenir au sujet, F# oblige l'utilisation de Else. Pour cette même raison (c'est risqué). Logique, mais peut-être casse pied à l'usage, il faut voir...

Souhaitant échapper au paradoxe de la secte, je ne vous donnerai pas un avis technique profond sur F#, mais je vous invite à lire une série de billets introduisant la syntaxe de F# pour vous faire vous-même une idée de la chose et je vous propose de venir laisser vos impressions en commentaires afin qu'on puisse en discuter. "Présentation de F#" par Laurent Le Brun

L'article est fait de petits billets, donc c'est facile à lire. En retour c'est un peu sommaire et pas forcément très expliqué, mais on ne peut pas tout avoir. En tout cas ça vous en dira suffisamment pour vous faire une idée, et surtout pour tester la syntaxe si vous téléchargez le langage (ce qui n'est pas nécessaire avec la bêta de VS2010, téléchargeable ici : http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx).

Je vous demandais plus haut de venir ajouter en commentaire votre avis sur F#, autant commencer par donner le mien penserez-vous. C'est oublier le "paradoxe de la secte" dans lequel cela me plongerait malheureux ! Mais comme c'est pour vous, j'ai testé un peu F#, échappant de justesse au paradoxe : ni je n'en connais rien, ni je n'en connais tout, en toute logique le paradoxe ne s'applique plus à mon cas :-) (c'est discutable, je sais, mais tout ce qui est discutable laisse chacun dans la possibilité de croire qu'il a raison !).

Bref, F# c'est jeune, c'est sympa, c'est de la "programmation fonctionnelle" (super classe et dans le vent), donc c'est bien. Peut-être. Ou peut-être pas. A petites doses, sous le couvert de LINQ, au d'extensions de C#, je ne dis pas. Exclusivement et en plat unique et principal, est-ce digeste ? J'ai un doute.

Si La syntaxe F# est "amusante" elle me semble peu lisible, peut-être par manque d'habitude. Toutefois quand on passe de C ou Java à C# on n'a pas cette impression, ni même de Delphi à Java ou réciproquement. Alors première "mauvaise impression" ou bien réalité ? Le problème c'est qu'il faudrait devenir bon en F# pour le comparer et savoir si on l'aime ou pas.. L'horrible et incontournable paradoxe de la secte qui revient ! Donc je suis obligé de rester au niveau subjectif et mon avis n'a rien de technique. De prime abord, je n'aime pas trop la syntaxe donc.

Par exemple il existe un mode "light" (devenant visiblement la "norme"). Dans ce mode la syntaxe devient plus "light" mais au prix que je trouve insensé de donner une valeur réelle à l'espace (le caractère espace, pas starwars hein..). C'est à dire que les blocs ne se déclarent plus avec des mots clés de début et de fin (ou les {} de C#), mais sont uniquement basés sur l'indentation du texte à l'espace près, les tabulations étant interdites.
Utiliser les espaces pour indenter c'est bien, ne pas pouvoir utiliser les tabulations c'est un peu embêtant, mais lorsque qu'on sait que ces espaces ont aussi un impact sur le code ça me dérange un peu. Que le nombre d'espaces devant une instruction puisse modifier son sens ou sa portée m'apparait un dangereux défaut. Mais ce n'est peut-être que par manque de pratique.

Quant à l'esprit du langage, dans la foulée des langages ML comme LISP, ce dernier m'a toujours paru trop abscons pour devenir populaire et jusqu'à lors, en 25 ans de métier aucun de ces langages n'a démenti cette impression et n'est devenu un blockbuster. F# sera-t-il celui qui me fera mentir ? Saura-t-il séduire les foules de développeurs en soif de nouveautés ? Ou bien restera-t-il juste un bel essai boudé par les mêmes informaticiens déjà gavés "jusqu'à là" de trucs nouveaux à apprendre sous .NET (et super géniaux comme Entity Framework, Silverlight, ASP.NET MVC, etc) ?

Wait and see dirais-je sagement pour ne pas trop me mouiller :-)

F# est plein de bonnes idées, mais sa syntaxe et son esprit me semblent difficilement compatible avec l'industrialisation du développement. Caml, OCaml, LISP et consors ne sont jamais sortis de leur cercle d'initiés. L'industrie du logiciel n'en a pas voulu. F# possède-t-il le petit "plus" qui lui permettra d'étendre son influence plus largement que ces prédécesseurs ? Nous verrons bien !

Alors j'attends vos avis et impressions !