Exporter (0) Imprimer
Développer tout
Ce sujet n'a pas encore été évalué - Évaluez ce sujet

Utilisation de GDI+ et de Visual Basic .NET

Visual Studio .NET 2003

Ken Getz
MCW Technologies, LLC Lien non Microsoft Site en anglais

Juin 2003

Résumé : Cet article fournit un exemple simple, mais explicite, des avantages tirés des fonctionnalités GDI+ de l'environnement .NET Framework dans le cadre d'une démonstration d'horloge. Il vous permet également d'améliorer vos compétences en Visual Basic .NET. (6 pages imprimées)

Téléchargement  Téléchargez l'exemple de fichier GDIPlus.msi.

Remarque   Pour démarrer l'exemple d'application, vous devez disposer d'une version de Microsoft Windows® dotée du Service Pack (SP) 2, version 1.0, de l'environnement .NET Framework. Le code de cet article est en Visual Basic® .NET et a été écrit et testé à l'aide de Visual Studio® 2002. Les tests ont été réalisés sur un système équipé de Windows XP Professional SP1.


Introduction

GDI+, un groupe de classes fourni par l'espace de noms System.Drawing dans .NET Framework, permet aux développeurs de créer facilement des applications graphiques en utilisant les capacités graphiques générées sous Windows. Cette simple application illustre la fonction de nombreux objets GDI+ et de leurs membres, y compris (mais sans limitation) l'utilisation des objets Pen (stylo), Brush (pinceau, couleur unie ou dégradé), Point, Rectangle, Ellipse et Region. C'est incroyable, le nombre de fonctionnalités GDI+ que l'on peut regrouper dans une seule démonstration d'horloge !

Utilisation de l'application

L'exemple d'application vous permet d'afficher l'heure au format analogique (comme illustré à la figure 1 ci-dessous) ou numérique.

Fig. 1
Figure 1. GDI+ gère toutes les tâches graphiques pour cette simple application d'horloge

Pour commencer, chargez la solution dans Visual Studio .NET et appuyez sur F5 pour charger et démarrer le projet. Par défaut, l'horloge s'affiche au format analogique, avec le bord du formulaire apparent, mais vous pouvez modifier ce comportement comme suit :

  • Redimensionnez le formulaire pour modifier la taille de l'horloge. La face avant de l'horloge est toujours centrée dans un cercle de largeur et de hauteur minimales dans la zone client du formulaire.
  • Double-cliquez sur le formulaire (ou cliquez avec le bouton droit de la souris et sélectionnez Show Frame dans le menu Context) pour modifier l'affichage du formulaire entourant la face avant de l'horloge.

Dans le menu Context, essayez les options suivantes :

  • Sélectionnez Analog ou Digital pour afficher l'horloge au format analogique ou numérique. Le format numérique est beaucoup plus simple, mais moins intéressant.
  • Sélectionnez Always on Top pour afficher l'horloge au premier plan par rapport aux autres fenêtres. (Si vous sélectionnez cette option, la propriété TopMost est sélectionnée.)
  • Sélectionnez l'option Run at Startup pour ajouter l'entrée appropriée dans le registre Windows et permettre ainsi le chargement automatique de l'horloge à chaque ouverture de session. (À vrai dire, lors des tests de l'application, l'un des testeurs s'est montré tellement enthousiaste qu'il a configuré son poste de travail afin que l'horloge soit toujours affichée sur son bureau. J'ai moi-même adopté ce mode. Nous regrettons vraiment tous les deux l'horloge de Windows NT®.)
  • Si vous décidez d'afficher l'horloge au format analogique, sélectionnez CountDown (et un intervalle) pour afficher une zone en forme de camembert indiquant un délai. Cette fonctionnalité, ajoutée à l'origine pour démontrer la méthode FillPie, vous permet de régler un chronomètre et d'afficher un indicateur clignotant lorsque le délai a expiré.
  • Si vous décidez d'afficher l'horloge au format analogique, sélectionnez Gradient (et un dégradé donné) pour afficher l'un des quatre dégradés présélectionnés pour l'horloge. Vous pouvez rechercher le code pour comprendre le fonctionnement des dégradés, mais chacun des quatre dégradés utilise des fonctionnalités GDI+ différentes.
  • Sélectionnez Fill Color et l'une des couleurs disponibles pour définir la couleur d'arrière-plan de l'horloge. Notez que ce menu illustre une autre utilisation des fonctionnalités GDI+. Ce menu personnalisé inclut un rectangle contenant les différentes couleurs. La création de menus personnalisés est un jeu d'enfant et elle est abondamment documentée. Vous pouvez utiliser cet exemple comme point de départ pour vos propres menus personnalisés et afficher des graphiques ou des images bitmaps.
  • Sélectionnez l'option Text Color, qui utilise la boîte de dialogue habituelle de sélection des couleurs pour habiller le texte de l'horloge.

Points concernant la conception de l'application

Outre le fait qu'il s'inspire d'une horloge pour expliquer l'interface GDI+, notre exemple d'application répartit ses fonctionnalités entre l'exemple de formulaire frmClock.vb (qui gère tous les menus et l'interface utilisateur pour l'application) et la classe Clock.vb (qui affiche l'horloge elle-même). En séparant le formulaire de l'horloge, vous découvrirez que vous pouvez réutiliser la classe Clock dans toute application requérant une horloge analogique. Pour cela, il vous suffit de lancer une instance Clock, de définir quelques propriétés, puis d'appeler la méthode appropriée de l'objet Clock à partir d'un événement Paint. Reportez-vous à la section Comment ça marche ci-dessous pour obtenir davantage d'informations.

Pour utiliser la classe Clock en dehors de cette application, ajoutez le fichier Clock.vb à votre projet. Le constructeur de l'objet Clock s'attend à ce que vous fournissiez un objet Form :

Dim MyClock As New Clock(Me)

Ensuite, dans le code qui prend en charge l'événement Paint de votre formulaire, appelez le code approprié pour afficher une version numérique ou analogique de l'horloge :

' Analog clock:
Draw(grfx As Graphics, Radius As Integer, Origin As Point)
' Digital clock:
Draw(grfx As Graphics, ClientRectangle As Rectangle)

Pour l'horloge analogique, vous devez fournir un rayon et une origine (les coordonnées du coin supérieur gauche de l'horloge et non celles du centre). Pour l'horloge numérique, vous devez simplement fournir un objet Rectangle décrivant la zone dans laquelle vous souhaitez afficher l'horloge (normalement, la propriété ClientRectangle d'un formulaire).

Vous vous attendez probablement à trouver un contrôle Timer sur le formulaire lui-même, effectuant la mise à jour de l'affichage à chaque seconde. C'est tout à fait concevable, mais ce n'est pas la méthode utilisée ici. La classe Clock utilise son propre chronomètre et invalide simplement son formulaire parent lorsqu'elle détermine qu'elle doit mettre à jour l'affichage. Grâce à cette technique, vous pouvez utiliser de multiples horloges, chacune contrôlant son heure propre, sans vous inquiéter des différents chronomètres. (Il est bon de noter que l'horloge n'invalide pas la totalité du formulaire parent, car cela serait trop coûteux et inutile. Au lieu de cela, l'horloge invalide uniquement la partie du formulaire affichant le contenu, c'est-à-dire celle décrivant l'horloge.) Lorsque la classe Clock invalide le formulaire parent (ou une partie de ce formulaire), le code de l'événement Paint du formulaire est de nouveau lancé et l'horloge s'affiche de nouveau.

Du fait que presque chaque méthode GDI+ requiert un objet Graphics comme contexte dans lequel afficher le résultat, le plus simple est de transmettre ce contexte Graphics comme paramètre à la méthode Draw de l'horloge à partir de l'événement Paint du formulaire. La procédure d'événement reçoit, comme paramètre, un objet PaintEventArgs qui inclut le contexte Graphics du formulaire comme l'une de ses propriétés. L'exemple de projet utilise du code similaire à celui présenté ci-dessous, à partir du gestionnaire d'événements Paint du formulaire :

DemoClock.Draw(e.Graphics, Radius, Origin)

Comment ça marche

Une fois que vous avez accepté le concept de méthode apparemment inversée selon laquelle l'application fonctionne (c'est-à-dire, le fait que le formulaire appelle la méthode Draw de la classe Clock à partir de son gestionnaire d'événements Paint et que le chronomètre de la classe Clock déclenche l'événement Paint du formulaire en invalidant une partie du formulaire), vous pouvez approfondir divers aspects du fonctionnement de l'horloge GDI.

Gestion du chronomètre

Étant donné que vous n'êtes pas sûr qu'un événement Tick du chronomètre va se produire à des intervalles parfaitement réguliers, vous ne pouvez pas régler la propriété Interval d'un chronomètre Windows sur exactement une seconde et espérer que l'horloge se mette à jour correctement. C'est pourquoi cette horloge utilise une autre technique. Elle règle l'intervalle sur 1/10 de seconde et, à chaque lancement du code, elle compare la seconde en cours à la seconde précédemment affichée. Si les valeurs sont différentes, la classe Clock sait qu'il est temps de mettre à jour l'affichage, puis déclenche (seulement à ce moment-là) l'invalidation du formulaire parent. Vous trouverez du code similaire au code ci-dessous dans le gestionnaire d'événements Timer_Tick de la classe Clock :

' In the class:
Private CurrentTime As DateTime = DateTime.Now

' In the Timer_Tick event handler:
Static dtmPrevious As DateTime

CurrentTime = DateTime.Now
If CurrentTime.Second <> dtmPrevious.Second Then
 dtmPrevious = CurrentTime
 ParentForm.Invalidate(InvalidRegion)
End If 

Le cauchemar de la trigonométrie

Vous pensiez probablement que vous n'utiliseriez plus jamais la trigo chère à vos années universitaires (si vous y avez même survécu), mais celle-ci est cruciale si vous souhaitez utiliser des objets circulaires. Dans cette application, la plus grande partie des tâches de formulation « mathématique » s'effectue en convertissant les angles autour de la face avant de l'horloge en points réels sur l'affichage du formulaire et vice versa, de sorte que le code puisse dessiner les lignes et les cercles nécessaires sur l'écran. Dans la classe Clock, les procédures GetPoint, GetHourDegrees, GetMinuteDegrees et GetSecondDegrees effectuent les tâches ingrates de conversion des coordonnées circulaires en coordonnées rectangulaires et vice versa. (Reportez-vous à la méthode GetHourDegrees pour obtenir des explications sur le fonctionnement du code.) Eh oui, vous allez devoir vous replonger dans les méandres de votre cerveau pour comprendre le comportement des fonctions Sin et Cos, mais n'oubliez pas le plus important (regardez bien la face avant de l'horloge au fur et à mesure que vous effectuez ces tâches) :

  • Le cercle (la face avant de l'horloge) est divisé en 360  unités identiques (appelées « degrés »), avec 0 degré lorsqu'il est 3 heures, 90 degrés quand il est midi, etc. Vous pouvez également traiter les angles sous forme de valeurs négatives, de sorte que -90 degrés correspondent à 6 heures, -180 degrés à 9 heures, etc.
  • Les emplacements rectangulaires sont mesurés comme des objets Point (ou des objets PointF, si vous avez besoin d'une précision à la virgule flottante), sous forme de coordonnées x et y. Dans cet exemple, le paramètre Origin transmis à la méthode Draw de l'horloge indique le coin supérieur gauche de la face avant de l'horloge (imaginez un rectangle dessiné autour de l'horloge dont l'origine serait le coin supérieur gauche). La coordonnée x augmente à mesure que vous vous déplacez vers la droite. La coordonnée y augmente à mesure que vous vous déplacez vers le bas.
  • La fonction GetPoint effectue la conversion d'un angle, d'un point central et d'une distance par rapport au centre en un point standard. En clair, cette procédure convertit les coordonnées polaires (circulaires) en coordonnées rectangulaires, en respectant la géométrie particulière de cette application.
  • Les méthodes GetHour/Minute/SecondDegrees convertissent les heures, les minutes et les secondes de l'heure et renvoient l'emplacement correspondant des aiguilles en degrés. Par exemple, à 21h00, la méthode GetHourDegrees renvoie -180 degrés.
  • La classe Clock utilise la valeur renvoyée par la méthode GetHourDegrees (et ses sœurs) pour effectuer le rendu des aiguilles de l'horloge. Le code appelle la méthode GDI+ DrawLine pour dessiner chaque aiguille, à l'aide d'objets Pen différents pour chaque aiguille. Le constructeur de la classe Clock définit des stylos spécifiques à utiliser lorsque l'horloge affiche l'heure, y compris les propriétés StartCap et EndCap de l'objet Pen. Grâce à ces propriétés, les aiguilles peuvent automatiquement inclure le bout d'une flèche d'un côté et la « bille » de l'autre.
  • La classe Clock appelle la méthode GetPoint, utilisant un rayon en quelque sorte abrégé, pour calculer les points auxquels dessiner les graduations des minutes et le nombre d'heures sur la face avant de l'horloge. (Le dégradé du tracé de progression de l'heure utilise également cette méthode, calculant les emplacements des points le long du bord du cercle.)

L'étude permettant de comprendre entièrement la trigonométrie utilisée par la classe Clock prendra un certain temps. Si seule la fonctionnalité GDI+ vous intéresse, vous pouvez tout simplement accepter qu'elle fonctionne, mais, dans ce cas, vous pouvez par contre vous pencher sur les méthodes appelant les membres GDI+ directement, dessinant des lignes et des cercles, des pinceaux, des stylos, etc.

Menus personnalisés

GDI+ permet également (et assez facilement) de gérer le dessin de chaque élément de menu à partir de votre code. Cet exemple utilise des techniques personnalisées pour le menu Fill Color, effectuant une sélection à partir d'une palette de couleurs décrite dans un tableau dans la classe du formulaire :

Private LightColors() As Color = _
{Color.LightBlue, Color.LightCoral, _
Color.LightCyan, Color.LightGoldenrodYellow, _
Color.LightGray, Color.LightGreen, _
Color.LightPink, Color.LightSalmon, _
Color.LightSeaGreen, Color.LightSkyBlue, _
Color.LightSlateGray, Color.LightSteelBlue, _
Color.LightYellow, Color.White} 

Afin de créer un menu personnalisé, vous devez définir la propriété OwnerDraw de l'élément de menu sur True (dans le code ou le concepteur). Cette propriété, une fois définie, permet de déplacer la responsabilité de la création et de l'affichage de l'élément de menu de votre code. Vous devez réagir aux événements DrawItem et MeasureItem de chaque élément de menu et votre code de gestion des événements doit fournir les informations nécessaires pour afficher chaque élément. L'exemple d'application dessine un petit rectangle de chaque couleur près du texte contenant le nom de chaque élément. Ce code utilise GDI+ pour afficher le rectangle et le texte.

Définition de styles dans le formulaire

Il n'est pas très difficile d'afficher un dégradé sur l'horloge (reportez-vous aux procédures GradientFill1, GradientFill2 et GradientFill3 Clock.vb), mais la mise à jour du dégradé nécessite une certaine puissance de traitement et c'est pourquoi il n'est pas recommandé de le faire à chaque seconde.

Pour résoudre ce problème, vous devez trouver un moyen de conserver une copie cachée du contenu du formulaire en mémoire et de mettre à jour uniquement la petite partie du formulaire qui doit être mise à jour à chaque seconde (normalement, uniquement l'aiguille des secondes et leur affichage au format numérique). Vous pourriez tout aussi bien le faire par vous-même, mais il est plus simple d'autoriser le formulaire à utiliser des « doubles tampons ». Vous pouvez faire en sorte que votre formulaire gère sa propre mise à jour, de sorte que l'affichage d'un dégradé en arrière-plan n'entraîne pas le clignotement de l'horloge à chaque seconde.

En outre, par défaut, il n'existe pas de code gérant l'événement Resize du formulaire. Si vous n'effectuez pas des tâches supplémentaires et appelez simplement la classe Clock à partir de votre formulaire, le redimensionnement du formulaire n'entraînera pas la mise à jour de l'affichage de l'horloge avant le passage à la seconde suivante et l'invalidation par la classe Clock de son parent.

Vous pouvez résoudre ces deux problèmes à l'aide de la méthode SetStyle de la classe Form. La procédure frmClock_Load inclut du code similaire à l'extrait ci-dessous (reportez-vous à la documentation concernant la méthode SetStyle pour obtenir davantage d'informations) :

Me.SetStyle(ControlStyles.ResizeRedraw, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or _
 ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True) 

Enregistrement des paramètres utilisateur

À la demande des testeurs qui ont décidé d'utiliser l'application GDIClock tous les jours, l'exemple d'application inclut du code permettant d'enregistrer et de restaurer vos paramètres vers un fichier de configuration. L'exemple inclut la classe AppSettings, qui gère les détails de la lecture et de l'écriture à partir du fichier de configuration (chemin Documents and Settings/<NomUtilisateur>/Application Data/GDIClock) et la classe RegSettings qui gère l'enregistrement des informations d'« exécution au démarrage » dans le registre. Si vous souhaitez savoir comment lire et écrire des informations dans des fichiers de configuration, jetez également un coup d'œil à ces classes.

Conclusion

Mission accomplie. Vous venez de mettre en application un exemple simple, mais explicite, des avantages tirés des fonctionnalités GDI+ de l'environnement .NET Framework pour une démonstration d'horloge et de découvrir une méthode vous permettant d'améliorer vos compétences en Visual Basic.



Dernière mise à jour le mardi 9 septembre 2003



Pour en savoir plus
Cela vous a-t-il été utile ?
(1500 caractères restants)
Merci pour vos suggestions.
Afficher:
© 2014 Microsoft. Tous droits réservés.