Dot.Blog

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

Silverlight 3 et DeepZoom

Deep Zoom, vous connaissez ? Probablement, c'est tellement sympa ! Si vous ne savez pas ce que c'est, voici en deux mots quelques explications : Deep Zoom est une technologie permettant d'afficher des images énormes sans avoir besoin d'attendre que tout soit téléchargé. On peut ainsi faire un "zoom profond" (deep zoom) sur une image, ou plus amusant, sur une composition d'images. Si vous connaissez Binq Maps, cela vous donne une bonne idée (partir d'une vision de la france depuis le ciel et zoomer jusqu'à voir le nombre de voitures garées devant chez vous).

Des tas de possibilités s'ouvrent si je vous dis que le logiciel Deep Zoom Composer est gratuit et qu'il permet de créer toute sorte de compositions et qu'il est même capable de créer des exécutables directement utilisables, voire même les sources complètes d'un projet Blend 3 !

C'est à partir de cette dernière option que j'ai créé la démo ci-dessous. Sur la base du projet créé par Deep Zoom Composer j'ai pu modifier certains comportements, améliorer certaines petites choses (notamment à l'aide de Behaviors dont certains sont déjà dans le projet). Vraiment incroyable et d'une rapidité fulgurante, si on fait abstraction du processus de création des milliers de fichiers nécessaires rien que pour la présente démo. En tout 4593 fichiers, 303 sous répertoires pour un poids total de 52,2 Mo. Du lourd donc. Surtout comparé au fichier exécutable Silverlight produit (le .xap) qui ne fait que 79 Ko.

Alors attention aux compositions géantes qu'on est tenté de concevoir, après il faut assumer niveau hébergement !

En revanche les possibilités d'interactions et de personnalisations au travers de Blend 3 sous Silverlight 3 sont innombrables. On sent un potentiel énorme, les idées d'utilisation un peu originales viendront certainement avec le temps une fois familiarisé avec la techno.

Ce billet ne présente pas de code, il suffit de disposer de Deep Zoom Composer, de quelques photos, de suivre la procédure et de bricoler un peu le C# du projet pour obtenir le même résultat. Le but est avant tout de vous parler de Deep Zoom, de vous dire que c'est vraiment fun, et qu'il ne vous reste plus qu'à l'essayer pour l'adopter !

nota: les photos font partie de celles que j'ai prise à Barcelone lors des TechDays 2008. Vous pourrez découvrir une photo d'une fière assemblée, en train de festoyer, et y reconnaître, parmi les autres participants de marque, notre Mitsu national, grand maître de ce genre de réunions gourmandes :-) Ne me cherchez pas sur la photo, il fallait bien quelqu'un pour immortaliser l'instant et tenir l'appareil photo...

Pour jouer avec l'exemple ci-dessus, faites du drag'drop pour déplacer le bloc de photos, cliquez pour zoomer ou utilisez les boutons en bas à droite (visibles quand la souris entre dans l'espace Deep Zoom), notamment le dernier à droite pour passer en full screen.

[silverlight:source=/SLSamples/DeepZoom/DeepZoomProject.xap;width=579;height=292]

Pour d'autres news vous connaissez le refrain : Stay Tuned !

WPF et Silverlight : composants orientés données dans les Toolkits

Trouver des ressources sur les technologies ou outils récents est toujours difficile. Par force, et c'est une Lapalissade, ce qui est plus connu est plus facile à connaître, et vice versa...

Le WPF Toolkit ainsi que Silverlight Toolkit sont des mines de composants ayant beaucoup de choses en commun : même base de code, évolutions rapides et donc releases fréquentes avec à chaque fois des ajouts d'importance. Suivre le ryhtme pourrait réclamer un job fulltime...

Voici un post de Delay's Blog qui résume les dernières ressources autour de la visualisation des données dans ces deux Toolkit : http://blogs.msdn.com/delay/archive/2009/07/19/my-new-home-page-enhanced-updated-collection-of-great-silverlight-wpf-data-visualization-resources.aspx .

Visualiser les données est essentiel à toute application de type gestion, et l'avalanche de nouveautés dans les Toolkits ciblant ce besoin permet désormais d'envisager sérieusement de créer de "vraies" application avec WPF et Silverlight. Par "vraies" j'entends des applications telles que nous en concevons tous régulièrement, et non des petits jeux ou des caroussels tournoyants qui font de magnifiques démos ou des sites Web publicitaires mais qui ne sont pas forcément des applications représentatives du travail habituel d'un développeur. Il est donc d'autant plus important de connaître ces extensions de WPF et de Silverlight et pour ce faire il faut trouver les bonnes explications...

A lire absolument donc, pour y piocher des articles, des astuces, ou de simples explications sur toute la partie affichage des données sous Silverlight ou WPF.

Avec ça, vous pouvez vous occuper jusqu'à la rentrée de septembre !

Mais Stay Tuned, car j'ai plein d'autres choses à vous dire d'ici là !

Créer un arbre des répertoires avec XML (et l'interroger avec LINQ to XML)

Pour les besoins d'un prochain billet je voulais obtenir une structure arborescente de test sous la forme d'un fichier XML. Une telle chose peut se faire à la main mais cela est fastidieux et à force de copier/coller les données seront trop semblables et donc peu utilisables pour des tests et du debug, ce qui est dommage puisque tel était le but recherché...

Pour générer des données aléatoires mais réalistes je possède déjà un outil fantastique (puisque c'est moi qui l'est fait :-) ), DataGen, un logiciel puissant mais qui n'est pas étudié pour générer des données dont la strucuture est récursive. Restait donc à concevoir un utilitaire pour satisfaire mon besoin de l'instant.

Je n'aime pas développer "pour rien" donc il fallait que cet utilitaire en soit bien un. Tout disque dur de développeur contient une telle quantité de données que l'ensemble des répertoires forment une superbe structure arborescente ni trop petite ni trop gigantesque. Exactement ce qu'il faut pour des tests ! Ainsi, l'outil devra fabriquer un fichier XML à partir de n'importe quel niveau de répertoire, la structure étant hiérarchique.

Je m'empresse donc de vous faire partager ce petit bout de code qui vous sera utile un jour ou l'autre je pense.

using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
 
namespace Enaxos.Tools.Xml
{
    /// <summary>
    /// This class can generate an XML file from a folder hierarchy.
    /// </summary>
    public static class DirToXml
    {
        /// <summary>
        /// Builds the tree document.
        /// </summary>
        /// <param name="dirname">The name of the starting folder.</param>
        /// <returns>a LINQ to XML <c>XDocument</c></returns>
        public static XDocument BuildTreeDocument(string dirname)
        {
            return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), 
                new XComment("Structure au "+DateTime.Now),new XElement("directories", BuildTree(dirname)));
        }
 
        /// <summary>
        /// Builds the tree (recursive method).
        /// </summary>
        /// <param name="dirName">Name of the starting folder.</param>
        /// <returns>a LINQ to XML <c>XElement</c> being the root (or 1st item) of the tree.</returns>
        public static XElement BuildTree(string dirName)
        {
            var di = new DirectoryInfo(dirName);
            var files = di.GetFiles();
            var dirsize = 0l;
            foreach (var file in files)
            {
                dirsize += file.Length;
            }
            var subdirs = di.GetDirectories();
            // each item is a "directory" having 5 attributes
            // name is the name of the folder
            // fullpath is the full path including the name of the folder
            // size is the size of all files in the folder (in bytes)
            // files is the number of files in the folder
            // subdirs is the count of possible sub directories in the folder
            var elem = new XElement("directory", 
                new XAttribute("name", di.Name), 
                new XAttribute("fullpath",dirName),
                new XAttribute("size", dirsize),
                new XAttribute("files",files.Count()),
                new XAttribute("subdirs",subdirs.Count()));
            foreach (var dinf in subdirs)
            {
                var elemDir = BuildTree(dirName + "\\" + dinf.Name);
                elem.Add(elemDir);
            }
 
            return elem;
        }
    }
}

Ce code peut être utilisé sans passer par un fichier disque puisqu'on récupère le document en mémoire. L'exemple ci-dessous montre comment appeler le code et comment interroger la structure via Linq To Xml pour connaître les répertoires vides (n'ayant ni fichier ni sous répertoire) :

   1:  var d = DirToXml.BuildTreeDocument(@"D:\WpfToolkit");
   2:  d.Save(@"d:\test.xml");
   3:  Console.WriteLine(d.ToString());
   4:  Console.WriteLine(new string('-',60));
   5:   
   6:  var q = from e in d.Descendants("directory")
   7:          where (int) e.Attribute("files") == 0
   8:               && (int)e.Attribute("subdirs") == 0
   9:               orderby (string) e.Attribute("fullpath")
  10:               select e.Attribute("fullpath");
  11:   
  12:  Console.WriteLine("Répertoires vides");
  13:  foreach (var element in q)
  14:    { Console.WriteLine(element); }
  15:  Console.WriteLine(string.Format("{0} répertoires vides",q.Count()));

 

A bientôt pour un prochain billet, so.. Stay Tuned !

Silverlight 3: Accélération GPU et cache image

Dans un récent billet sur les Pixel Shaders (effets bitmap) j'avais, par un raccourci de pensée un peu rapide, associé trop facilement les capacités d'accélération graphique GPU de Silverlight 3 avec celles de WPF. Or il existe des nuances très sensibles entre les deux implémentations.

Par souci de rigueur j'ai modifié le billet original en lui ajoutant les précisions nécessaires mais les corrections de billets ne sont pas propagées par feedburner qui gère une parite des flux RSS des abonnés à mon blog. Et pire, ceux qui ont déjà téléchargé le billet sous Outlook ou un autre news reader n'ont aucune chance de voir la correction. Ne reste plus qu'à créer un nouveau billet pour m'assurer que l'info est passée.. Voici la notice ajoutée au billet cité plus haut :

--extrait--

Quelques mots sur l'accélération GPU : Même si ce n'est pas le sujet du présent billet, il s'agit d'une nouvelle feature très intéressante de SL3. On a tendance hélas (et j'ai été le premier à me faire "avoir") à trop vite faire le parallèle avec WPF et à transposer ce que fait l'accélération sous ce dernier. En fait, le support de l'accélération GPU de SL3 est pour l'instant assez limité et ne se met en route que de façon volontaire. Elle ne s'applique pas aux effets bitmaps ni aux nouveaux flux vidéos qui restent traités par le CPU.

Malgré tout le système d'accélération graphique peut rendre d'immense service, dans certains de mes tests sur l'animation d'images assez grandes j'ai pu voir mon PC passer de 88% d'utilisation CPU (double coeur Asus plutôt rapide) à moins de 4% ! Ce qui est énorme. Mais pour profiter de l'accélération il faut aboslument en connaître le fonctionnement et les limitations.

Je vous invite ainsi à lire l'excellent billet de Andras Verlvart sur la question. Il est assorti d'une application exemple en live qui permet de bien voir l'effet des différentes possibilités d'accélération et leur impact sur le CPU et le GPU. Cet article est en anglais, pour ceux qui préfère la lecture en français, David Rousset a écrit un billet qui fait le point sur cette même question.

--fin d'extrait--

Espérant ainsi avoir rétabli la précision technique nécessaire à propos de cette nouvelle possibilité de Silverlight 3, et incidemment, vous avoir donné envie de vous familiariser avec et de l'adopter dans vos développements,

Stay Tuned pour d'autres nouvelles !

« 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 !