Le forum de Minecraft-France va définitivement fermer ses portes. Celui-ci restera en lecture seule mais vous ne pourrez plus y apporter de nouveaux topics. Nous vous invitons à nous rejoindre sur le Discord de Minecraft-France qui permet de présenter vos projets, discuter avec la communauté etc.. Merci à tous d'avoir fait vivre ce forum de nombreuses années.
Pour nous rejoindre sur Discord, Cliquez ici
Bonjour bonsoir, mesdames et messieurs, grands, moyens, petits, beaux, moins beaux, pas beaux, gentils, méchants, faux-méchants comme Kylo Ren, cette introduction commence à devenir beaucoup trop longue et lourde. (cmb)
Vous êtes sur mon tutoriel pour apprendre à développer un lanceur pour Minecraft pour ses dernières versions (1.7 et supérieur, je pense), presque à partir de zéro.
Je dis presque, car je ne vais pas expliquer toutes les notions, raisons pour laquelle je vous conseille d'aller voir le tutoriel dont je vais parler plus bas.
Je vous souhaite une bonne lecture :)
I. Introduction II. Les bases
a. Mise au point b. Les outils nécessaires
III. Création du projet
a.Réglages du projet b. Derniers réglages avant de commencer
IV. Optionnel - déboguer plus facilement
a. Préparation de la classe b. Premier débogage
V. La classe LauncherVariables VI. Les graphismes
a. Les choses à éviter b. L'image de fond c. Les polices d'écriture personnalisées
1. Téléchargements 2. Utilisation
d. Bouton de fermeture et de réduction personnalisés
1. Transitions Dot Net 2. Les évènements de la souris 3. Résultat
VII. Optionnel - l'organisation
a. Les régions b. Les commentaires
VIII. Finitions de la Control Box
a. Fermeture et réduction b. Animations
IX. Les graphismes (deuxième partie)
a. Le titre et le logo de la fenêtre b. Optionnel - le label personnalisé c. Des contours d. Déplacement de la fenêtre
X. L'authentification
a. Préparation
1. Les zones de texte 2. Le bouton de connexion
b. Utilisation de MojangAPI c. AuthManager et threads
I. Introduction
Il existe déjà plusieurs tutoriels disponibles sur Internet, et même sur le forum.
Cependant, au vu des tutoriels proposés, je tenais à partager mon expérience de développement, et donc, ma manière de faire.
Je vais donc tenter de vous apprendre à créer un lanceur (entendez « Lanceur ») pour Minecraft, compatible avec les dernières versions de ce dernier.
II. Les bases
a. Mise au point avant de démarrer
J’estime ici que vous savez déjà ce qu’est un langage de programmation. Si non, je vous invite à jeter un œil sur l’article dédié de Wikipédia. Cependant, si vous ne savez même pas ce qu'est un langage de programmation, vous aurez probablement du mal à suivre ce tutoriel dans son intégralité.
Pour ceux qui n’ont jamais développé ou qui ne connaissent pas Visual Basic .NET, C# .NET, ou plus globalement Visual Studio, je vous invite à lire les premiers chapitres du tutoriel VB .NET sur OpenClassrooms, histoire que vous ne soyez pas trop vite dépassés.
Vous pouvez aussi lire le tutoriel en entier, cela ne peut pas vous faire de mal.
Je vais cependant vous aider dans l’installation des outils nécessaires, car le tutoriel d’OpenClassrooms commence à se faire un petit peu vieux. :3
b. Les outils nécessaires
Pour commencer le développement de notre lanceur, nous avons besoin d’un IDE. En l’occurrence, nous aurons besoin de Visual Studio Express, la version gratuite de l’environnement de développement de Microsoft.
ÉDITION - Dracoctix a mentionné, dans les commentaires, une version plus complète de Visual Studio. Elle est gratuite et plus fonctionnelle, vous pouvez la télécharger et l'utiliser à la place de Visual Studio Express. Cliquez ici.
Figure 1 - Téléchargement de Visual Studio Express 2015 pour Windows Desktop
Descendez plus bas sur la page, sous la section « Téléchargements Visual Studio », puis cliquez sur « Visual Studio 2015 » dans le menu latéral, et enfin, sur « Express 2015 pour Desktop ».
Sélectionnez votre langue, puis cliquez sur le bouton de téléchargement.
Suivez les étapes, téléchargez l’exécutable, installez Visual Studio, puis ouvrez-le.
III. Création du projet
a. Réglages du projet
J’ai créé un projet WinForms nommé Lanceur Minecraft.
À noter › Si vous ne savez pas comment créer un projet, je vous invite à lire le tutoriel référencé dans l'introduction. Prenez garde à créer un projet WinForms et non WPF, ce n'est pas la même chose :)
Je vous invite à ouvrir les propriétés du projet en double-cliquant sur My Project dans l’explorateur de solutions.
Modifiez le contenu de la zone de texte sous Espace de noms racine, de manière à ce que le contenu soit aussi court et clair que possible.
Dans mon cas, c’était « Lanceur_Minecraft » et je l’ai modifié en « Launcher ».
J’ai appelé mon projet « Lanceur Minecraft » car je n'ai pas de vrai nom pour celui-ci.
Cependant, si votre lanceur a un nom, vous pouvez l’utiliser pour l’espace de noms racine.
En guise d'exemple, j’ai dû créer un lanceur qui devait s’appeler « Mineteam ». J’ai donc utilisé le nom « Mineteam » pour l’espace de noms racine.
Attention › Gardez en tête que tous vos noms de variables et de classes devraient être en anglais. Il est très peu recommandé de les écrire en français, malgré le mauvais exemple du tutoriel d’OpenClassrooms.
Choisissez ensuite la version du Framework sur laquelle vous comptez développer. Dans mon cas, je vais développer sous la 4.5.
Cette dernière est native sur Windows à partir de Windows 8 ; mais prenez en compte que les utilisateurs de votre lanceur doivent impérativement avoir au minimum la version que vous choisirez pour pouvoir ouvrir le lanceur. Si vous comptez distribuer votre lanceur à des gens sous Windows Vista (vous ne devriez pas), vous devriez choisir une version inférieure.
Cochez maintenant la case Application à instance unique, et choisissez À la fermeture du dernier formulaire comme mode d’arrêt.
Enfin, cliquez sur Informations de l’assembly. Une boîte de dialogue apparaît : vous pouvez la remplir avec les informations que vous souhaitez.
Figure 2 - Informations de l'assembly de mon lanceur
Validez en cliquant sur OK, puis sauvegardez le projet (Ctrl+S).
b. Derniers réglages
Il est toujours bien de renommer ses contrôles et formulaires afin d’avoir une meilleure organisation. Je vous invite donc à renommer votre formulaire Form1 en Main.
Pour le renommer, cliquez sur Form1.vb dans l’Explorateur de solutions et appuyez sur F2.
Si une boîte de dialogue s’affiche, cliquez sur Oui : elle va renommer toutes les références à votre fenêtre comme nous le voulons.
Si elle ne s’affiche pas, c’est que vous avez déjà changé le nom de votre formulaire dans ses propriétés.
À noter › En anglais, « Main » veut dire « principal ». C’est une habitude et une convention chez les développeurs que les classes principales se nomment Main.
Je vous invite maintenant à vous rendre dans les propriétés de votre Main.
Voici les propriétés que nous allons changer maintenant, dans l’ordre (par catégories) :
FormBorderStyle à FixedSingle – cela va empêcher notre utilisateur de modifier la taille de la fenêtre.
Text à Lanceur Minecraft – ou ce que vous voulez : c’est le titre qui va s’afficher en tant que titre du formulaire. Je vous invite cependant à mettre quelque chose de sobre et clair, il est toujours désagréable de lire « Minecraft Launcher v3.2.08 by Xxx_MrAlbertoDu64_xxX » en nom de fenêtre. J’exagère un peu, mais en lisant ma signature vous verrez que je n’ai aucune pitié pour les kikoos.
Size à 1000 ; 600 – ce sera la taille de la fenêtre. Vous pouvez mettre ce que vous souhaitez, je serai moi-même peut-être amené à la modifier par la suite.
StartPosition à CenterScreen – ça va être la position de départ de la fenêtre sur notre écran.
MiximizeBox à False – cela désactive le bouton Agrandir de la fenêtre.
MinimizeBox à False – pareil, avec le bouton Réduire.
Nous serons peut-être à nouveau amenés à modifier ces propriétés, mais, pour le moment, nous avons modifié les bases.
Figure 3 - État du projet après les modifications
IV. Optionnel - déboguer plus facilement
a. Préparation de la classe
Ceux qui ont déjà programmé le savent, déboguer un programme (trouver les erreurs dans le code qui causent un disfonctionnement, autrement dit, les bugs) peut s’avérer quelquefois plutôt difficile.
Dans l’optique d’anticiper d’éventuels problèmes, nous allons écrire une classe partagée (qui n’aura donc pas besoin d’être instanciée) qui nous permettra de loguer des informations au fur et à mesure que le programme se déroule.
Créez donc un nouveau dossier en effectuant un clic droit sur le nom de votre projet dans l’Explorateur de solutions, puis en sélectionnant Ajouter > Nouveau dossier. Nommez-le « Core ».
Une fois cela fait, créez une classe à l’intérieur de ce dossier que vous nommerez « Logger ».
Figure 4 - Contenu de la classe automatiquement créée
Nous allons donc créer plusieurs fonctions que je ne vais pas expliquer, qui vont permettre d’écrire quelque chose dans un fichier sous la forme [Heure] [Niveau d’alerte] <message>.
Vous pouvez copier-coller le code ci-dessous dans votre classe, je vais juste expliquer quelques petites choses.
Code (vbnet):
Imports System.Text
PublicClass Logger
PrivateShared _LogWriter As IO.StreamWriter PrivateShared _LogFile AsString PrivateShared _DefaultColor As ConsoleColor = ConsoleColor.White PublicEnum Levels
Normal =1
Debug =2 [Warning]=3 [Error]=4 EndEnum
' ------------------------------------------------------------------------------- ' -------- [ PUBLIC ] ----------------------------------------------------------- ' -------------------------------------------------------------------------------
''' <summary> ''' Writes a new line ''' </summary> <DebuggerHidden()> PublicSharedSub NewLine()
Console.WriteLine() EndSub
''' <summary> ''' Writes a normal text in the console, in green. ''' </summary> ''' <param name="Text">The text.</param> <DebuggerHidden()> PublicSharedSub Normal(ByVal Text AsString)
Logger.WriteLine(Levels.Normal, Text) EndSub
''' <summary> ''' Writes a debug text in the console, in gray. ''' </summary> ''' <param name="Text">The text.</param> PublicSharedSub Debug(ByVal Text AsString)
Logger.WriteLine(Levels.Debug, Text) EndSub
''' <summary> ''' Writes a warning text in the console, in yellow. ''' </summary> ''' <param name="Text">The text.</param> <DebuggerHidden()> PublicSharedSub Warning(ByVal Text AsString)
Logger.WriteLine(Levels.Warning, Text) EndSub
''' <summary> ''' Writes an error text in the console, in dark red. ''' </summary> ''' <param name="Text">The text.</param> <DebuggerHidden()> PublicSharedSub[Error](ByVal Text AsString)
Logger.WriteLine(Levels.Error, Text) EndSub
''' <summary> ''' Writes an exception text in the console, in dark red. ''' </summary> ''' <param name="Exception">The exception to log.</param> <DebuggerHidden()> PublicSharedSub Exception(ByVal Exception As Exception)
Logger.NewLine()
Logger.Error(Logger.GetSeparator(Levels.Error))
Logger.Error(Exception.Message)
Logger.Error(Exception.StackTrace)
Logger.Error(Exception.StackTrace)
Logger.Error(Logger.GetSeparator(Levels.Error))
Logger.NewLine() EndSub
PrivateSharedFunction GetSeparator(OptionalByVal Level As Levels = Levels.Normal) Dim Header AsString="" For i = Header.LengthTo Console.WindowWidth- Level.ToString.Length-Date.Now.ToString("HH:mm:ss").Length-8
Header &="-" Next Return Header EndFunction
''' <summary> ''' Writes a line in the logger with the specified color. ''' </summary> ''' <param name="Level">The line's level.</param> ''' <param name="Text">The text.</param> PrivateSharedSub WriteLine(ByVal Level As Levels, ByVal Text AsString) Dim Timestamp AsString=Date.Now.ToString("HH:mm:ss") ' If Not (Level = Levels.Debug And LauncherVariables.DEBUG = False) Then Console.WriteLine("[{0}] [{1}] {2}", Timestamp, Level.ToString, Text)
Logger.WriteInFile(String.Format("[{0}] [{1}] {2}", Timestamp, Level.ToString, Text)) EndSub
''' <summary> ''' Logs the logs in a file. ''' </summary> PrivateSharedSub WriteInFile(ByVal Text AsString) If IsNothing(Logger._LogWriter)Then IfNot IO.Directory.Exists("logs/")Then IO.Directory.CreateDirectory("logs/")
Logger._LogFile ="logs/"&Date.Now.ToString("MM-dd-yyyy-HH-mm")&"-logs.txt"
Logger._LogWriter =New IO.StreamWriter(Logger._LogFile, True, Encoding.UTF8) EndIf
Try
Logger._LogWriter.WriteLine(Text)
Logger._LogWriter.Flush() Catch ex As Exception MsgBox(ex.ToString) EndTry EndSub
''' <summary> ''' Finalizes the logger and close it properly. ''' </summary> <DebuggerHidden()> PublicSharedShadowsSub Finalize()
Logger._LogWriter.Close()
Logger._LogWriter.Dispose() EndSub
EndClass
La ligne Public EnumLevels ainsi que le contenu de son bloc sont une énumération : cela permet de récupérer des valeurs à l’aide d’un nom. C’est un peu comme des variables, sauf que les valeurs ne changent pas et les noms sont regroupés sous un autre nom.
À noter › Certains noms sont entre crochets. Ce sont des mots clés réservés à notre IDE pour pouvoir identifier certains types, variables globales, etc… les mettre entre crochet permet de malgré tout utiliser le nom voulu.
Ici, on peut accéder à la valeur de Debug en écrivant Logger.Levels.Debug. Cela permet généralement la facilitation des conditions.
Dans notre cas, nous n’en avons pas vraiment besoin puisque j’ai choisi de créer une fonction pour chaque niveau d’alerte. Nous pourrons donc écrire du debug en appelant Logger.Debug("Test"), au lieu de Logger.WriteLine(Levels.Debug,"Test").
Attention › J’ai mis la fonction WriteLine en Private, cela change son niveau d’accès : vous n’aurez pas accès à la fonction en dehors de la classe. C’est un concept de Programmation Orientée Objet (POO), vous pouvez en apprendre plus sur le tutoriel que je vous ai donné au début du tutoriel.
Figure 5 - Contenu de la classe Logger
Attention › Si vous êtes attentifs, vous avez remarqué qu’une classe LauncherVariables a été créée. Je vous expliquerai ce qu’il en est plus tard.
b. Premier débogage
Je vous invite à vous rendre sur votre Main et à double-cliquer sur la fenêtre.
Cela va vous créer un Évènement dans le fichier correspondant à la classe Main.
Écrivez « Logger.Debug("Test") » dans cet évènement, puis lancez le programme avec la touche F5.
Figure 6 - Première ligne dans notre premier évènement
Sous vos yeux ébahis (ou peut-être pas), une fenêtre blanche s’affiche. Vous pouvez fermer le programme.
Pour les plus à l’ouest d’entre vous, vous vous demandez pourquoi je vous ai fait ouvrir la fenêtre, et/ou pourquoi ne s’est-il rien passé alors que nous avons écrit une ligne de code.
Pour ceux qui suivent et qui ont analysé le code de la classe Logger, vous avez déjà ouvert le répertoire de votre solution à la recherche du fichier log.
Comment ça non ?
Effectuez un clic droit sur le nom de votre projet dans l’Explorateur de solutions et cliquez sur Ouvrir dans l’Explorateur de fichiers. Rendez-vous dans bin > Debug > logs. Là, étincelant, votre tout premier log. Vous pouvez l’ouvrir.
Figure 7 - Tout premier log
Si vous avez suivi, vous savez qu’à chaque action un peu importante dans votre programme, vous appellerez la fonction Debug de votre Logger afin d’écrire dans ce fichier des informations de debug.
V. La classe LauncherVariables
Nous allons vite passer dessus, c’est simplement une classe partagée (comme la classe Logger) qui va contenir des variables globales au lanceur.
Par exemple, la mienne contient la ligne Public ConstDEBUG As Boolean= True qui permet de savoir si l’on veut déboguer ou non.
Je vous invite donc à créer cette classe et à y mettre cette ligne.
Figure 8 - La classe LauncherVariables
Attention › Cette classe et son contenu seront amenés à changer avec le futur système de paramètres.
VI. Les graphismes
Histoire de ne pas trop vous faire languir, nous allons passer à l’aspect graphique du lanceur.
Cependant, malgré les graphismes que je vais adopter, vous pouvez très bien créer les vôtres, et totalement innover. Je ne suis pas excellent en graphismes, je vais donc faire le minimum nécessaire.
Vous avez sans doute déjà vu pas mal de lanceurs, certains jolis, certains moches. Pratiquement aucun n’adopte une charte graphique, et tous - ou presque - ont vu leur design créés à coup de « tiens c’est pas mal, je vais laisser ça comme ça ».
Eh bien, contre toute attente... je vais faire pareil. :3
Je ne vais pas me plier aux règles d’une charte graphique comme le Flat UI ou le Material UI de Google car ce serait trop long, mais sachez que vous, qui lisez ce tutoriel, et qui créez un lanceur pour votre serveur, devriez respecter une de ces chartes pour que votre lanceur soit uniforme et agréable à l’œil.
Cependant, vous pouvez aussi me recopier. Vous avez le droit.
a. Les choses à éviter
Les textes en gras
Quelque chose d’extrêmement désagréable à voir sur un formulaire quelconque, que ce soit un lanceur Minecraft ou autre, est un texte en gras mal géré. Si vous comptez mettre quelque chose en évidence, utilisez des polices comme Roboto Medium, mais ne mettez pas du gras, car les polices rendent souvent très mal sur un formulaire.
Les couleurs flashy
Vous vous en doutez certainement, mais mettre du vert fluo en image de fond sur votre lanceur n’est pas une bonne idée.
Les fautes d’orthographe
Une faute de frappe c’est vite arrivé, mais si vous n’êtes pas capables d’aligner 10 mots sans faire une faute d’orthographe, utilisez un p*tain de correcteur orthographique. Rien de plus désagréable que de lire un texte rempli de fautes, et je ne suis pas le seul de cet avis. De plus, un rendu sans faute rendra votre lanceur plus professionnel.
Les mauvais alignements
Quand vous alignez des contrôles (que nous verrons plus tard) tels que des étiquettes (labels), boutons, ou autres éléments graphiques, veillez à ce qu’ils soient alignés (vous pouvez vous aider des valeurs numériques des propriétés Size et Location, de la propriété Dock, et de la barre d'outils Disposition)
Évitez les ombres et le biseautage
Hormis sur les cadres (panels), les ombres sont à proscrire. De même, le biseautage dans un nom de serveur écrit à la va-vite sur une version tout à fait légale de Photoshop.
b. L’image de fond
J’ai l’intention d’utiliser, pour mes graphismes, une image de fond. Mais sachez qu’une image de fond possède un gros désavantage : elle rendra votre lanceur plus lourd.
Que ce soit au téléchargement ou à l’exécution, une image rendra plus lourd votre lanceur. Si vous n’avez pas un ordinateur très puissant (comme moi), vous pourriez voir des défauts d’affiche lors de l’affichage, de la fermeture ou du déplacement de votre fenêtre.
Pour éviter cela un maximum, prenez une image compressée (pas trop quand même) et qui fait la taille exacte de votre lanceur : ni trop grande, ni trop petite.
Voici l’image de fond que j’ai choisie :
Vous pouvez la télécharger en effectuant un clic droit, puis "Enregistrer sous"
C’est une image que j’ai trouvée sur Google. Je l’ai réduite à une taille de 1000 * 600 pixels et floutée à 15%.
Vous êtes évidemment libre de choisir votre propre image, comme par exemple, une capture d’écran de votre serveur (un endroit intéressant, pas l’herbe, hm ?). Je vous conseille également le flou.
Rendez-vous donc dans les propriétés de votre Main et cliquez sur BackgroundImage. Sélectionnez le bouton radio du bas, et cliquez sur Importer. Choisissez votre image, puis validez.
Enfin, mettez la valeur Zoom à BackgroundImageLayout, cela aura pour effet de cadrer l’image dans votre fenêtre (notez que vous n’avez pas besoin de faire ça si vous avez suivi mes conseils).
Si ça ne va pas avec votre image, essayez Stretch (« étirer »), ou si vous avez choisi un modèle (pattern) tel que l’image d’un cube de terre, choisissez Tile (« tuile », soit « répétition »).
Pour un meilleur rendu, j’ai choisi de finalement retirer les bordures de la fenêtre. Pour cela, mettez la valeur « None » à FormBorderStyle, et n’oubliez pas de remettre la taille de la fenêtre à1000 * 600, car elle a été modifiée.
Enfin, mettez un Panel (qui se trouve dans la boite à outils, si vous ne la trouvez pas, ouvrez-la en appuyant sur Ctrl+W, puis X, ou en allant dans Affichage > Boîte à outils) dans votre formulaire.
Accédez aux propriétés du Panel et changez ces valeurs :
Name à PNL_TOP – Il est toujours aussi important de modifier les noms des contrôles.
Dock à Top – Ainsi, le Panel sera lié au haut de notre fenêtre.
Size à 1000 ; 38 – De toute façon, vous ne pouvez pas modifier le 1000. Le 38 correspond à la hauteur du Panel.
BackColor à 153 ; 0 ; 0 ; 0 – Cela correspond à un fond noir avec une opacité de 60% (60 / 100 * 255).
c. Les polices d’écriture personnalisées
1. Téléchargements
Afin de parvenir à une meilleure personnalisation, vous allez être amenés à utiliser vos propres polices.
Je vous conseille donc de télécharger celles-ci :
Material Icons - c'est une police d'écriture créée et fournie par Google qui contient un symbole à chaque caractère. Elle est extrêmement utile (et jolie), je vais beaucoup l'utiliser dans ce projet.
Roboto - c'est aussi une police d'écriture de Google, et elle rend elle aussi très bien. Je vous la conseille également.
À noter › Pour télécharger Roboto, cliquez sur la flèche en haut à droite du compteur, et cliquez sur Zip File.
Figure 9 - téléchargement de la police Roboto
2. Utilisation
Afin de les utiliser, vous allez devoir commencer par les importer dans votre projet.
Pour cela, allez dans les propriétés du projet (My Project) et cliquez sur l’onglet Ressources. Faites un glisser-déposer des polices précédemment téléchargées dans la zone blanche.
Figure 10 - Importation de la police d'écriture Material Icons Regular
Cela fait, créez une classe dans le dossier Core, que vous nommerez FontManager.
Collez-y ce code, que vous n’avez pas besoin de comprendre. Sachez juste qu’il permet de récupérer un objet de type Font grâce à un tableau de Byte.
PrivateShared _FontCollection As PrivateFontCollection
''' <summary> ''' Get an instance of a given font. ''' </summary> ''' <param name="Font">The font to use, for example, in the resources.</param> ''' <param name="Size">The size of the font, in pixels. 12 by default.</param> ''' <param name="Style">The style of the font, regular by default.</param> PublicSharedFunction GetFont(ByRef Font()AsByte, OptionalByVal Size AsSingle=12, OptionalByVal Style As FontStyle = FontStyle.Regular)As Font If FontManager._FontCollection IsNothingThen LoadFont(Font) ReturnNew Font(FontManager._FontCollection.Families(0), Size, Style)
EndFunction
''' <summary> ''' Load a font in the FontCollection ''' </summary> PrivateSharedSub LoadFont(ByRef Font()AsByte) Try
FontManager._FontCollection =New PrivateFontCollection ' On initialise la collection Dim FontPointer As IntPtr = Marshal.AllocCoTaskMem(Font.Length)' On crée un pointeur vers une partie de la mémoire
Marshal.Copy(Font, 0, FontPointer, Font.Length)' On copie la police dans la mémoire
FontManager._FontCollection.AddMemoryFont(FontPointer, Font.Length)' On ajoute la police se trouvant dans la mémoire dans la collection
Marshal.FreeCoTaskMem(FontPointer)' On libère la mémoire précédemment utilisée
Catch ex As Exception
Logger.Exception(ex)' Nous loggons notre exception EndTry EndSub
EndClass
À noter › Si vous souhaitez malgré tout comprendre le code, il est en partie commenté.
Créez maintenant une étiquette (Label) que vous placerez dans le PNL_TOP. Changez les propriétés suivantes :
BackColor à Transparent (ou 0 ;0 ;0 ;0) – de manière à rendre son fond transparent
Cursor à Hand – afin que notre souris se transforme en main
Name à LBL_CLOSE – afin de l’identifier dans le code
Dock à Right – c’est important, cela sert à coller l’étiquette à droite du Panel.
Size à 38 ; 38 – pour que cela forme un carré de côté 38 px.
Votre bouton Fermer est presque prêt. Rendez-vous dans l’éditeur de code, et dans l’évènement Main. S’il y a toujours la ligne de debug, vous pouvez l’enlever. Ajoutez-y ces lignes :
La première permet de charger la police grâce au FontManager. Le 15 est la taille de la police. La seconde permet d’écrire le caractère unicode correspondant à la croix dans la tableau des caractères de la police (donc E5CD).
À noter ›Le préfixe &H permet de rentrer un nombre de base 16, c’est-à-dire un nombre hexadécimal. En C#, on pourrait directement écrire « \ue5cd » dans une chaîne de caractère ; en VB, on utilise la fonction ChrW pour récupérer ce caractère.
À noter › Vous pouvez vous rendre sur la page GitHub de la police d’écriture pour avoir plus de logos. Vous pouvez également installer la police sur votre PC et ouvrir charmap.exe (Windows+R, charmap, puis touche Entrée) pour les voir. Le code correspondant est affiché en bas.
Figure 11 - La fenêtre Exécuter et la fenêtre Charmap
Vous pouvez maintenant ouvrir le programme avec F5. Surprise ! Une jolie croix apparaît en haut à droite. Mais elle n’est pas encore fonctionnelle.
d. Bouton de fermeture et de réduction personnalisés
1. Transitions Dot Net
Nous allons devoir utiliser ce qu’on appelle une bibliothèque. Sortez donc de chez vous, rendez-vous dans la bibliothèque la plus proche et kidnappez la bibliothécaire.
Une bibliothèque est en fait une liste de fonctions déjà écrites par quelqu’un d’autre, qui nous mâche bien le travail, parce qu’on est des flemmards.
Téléchargez donc la bibliothèque « Transition Dot Net » à cette adresse, et extrayez-la vers un dossier sur votre disque dur.
Rendez-vous dans les propriétés de votre projet, onglet Références, cliquez sur Ajouter, Parcourir, trouvez le fichier Transitions.dll là où vous l’avez extrait, puis validez en cliquant sur OK.
Félicitations, vous venez d’importer votre première bibliothèque ! :)
Cette dernière va en fait nous servir à effectuer des transitions (oui c’est moi, Captain Obvious) afin de rendre notre bouton plus joli.
2. Les évènements de la souris
Je vous invite maintenant à cliquer sur l’icône en forme d’éclair en haut des propriétés de l’étiquette et à double-cliquer sur la zone de texte à droite de MouseEnter, en bas.
Figure 12 - onglet des évènements de l'étiquette
Cela nous amène dans l’éditeur de code, dans la méthode correspondant à l’évènement appelé lorsque l’on survole l’étiquette avec la souris.
Mettez-y le code suivant :
Code (vbnet):
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#e57373"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(100, 0, 0, 0), New TransitionType_Linear(250))
Ouvrez maintenant l’évènement MouseLeave et insérez-y ce code :
Code (vbnet):
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(0, 0, 0, 0), New TransitionType_Linear(250))
Attention › N'oubliez pas d'importer Transitions dans votre classe. Sans l'import, il va y avoir des erreurs : placez Imports Transitions en début de fichier.
Analysons ces lignes.
La première permet d’effectuer une transition linéaire d’une durée de 250 millisecondes de la valeur actuelle de la propriété ForeColor à une nouvelle valeur, soit la couleur « #e57373 » qui correspond à une nuance de rouge des codes couleurs de Google.
À noter › Je vais souvent réutiliser ces codes, vous devriez vous aussi garder cette page à portée de main, et même, en favoris.
La seconde permet d’effectuer la même transition, mais de la couleur de fond, et change simplement l’opacité en l’augmentant à 39%.
Les lignes de l’évènement MouseLeave font la même chose en sens inverse.
Ouvrez maintenant votre lanceur avec F5, et passez votre souris sur votre bouton de fermeture. N’est-ce pas magnifique ?
Je vous laisse maintenant copier ce bouton pour créer un bouton de réduction. Pour cela, vous devez savoir :
Les évènements sont donc les mêmes que pour l’étiquette de fermeture avec certaines valeurs changées. Je vous laisse vous débrouiller. :3
3. Résultats
C’est bon ? Vous avez finit ? Allez, je vous file le code.
Code (vbnet):
Imports Transitions
PublicClass Main
PrivateSub Main_Load(sender AsObject, e As EventArgs)HandlesMyBase.Load Me.PNL_TOP.Font= FontManager.GetFont(My.Resources.MaterialIcons_Regular, 15) Me.LBL_CLOSE.Font= PNL_TOP.Font Me.LBL_CLOSE.Text=ChrW(&HE5CD) Me.LBL_REDUCE.Font= PNL_TOP.Font Me.LBL_REDUCE.Text=ChrW(&HE5CF) EndSub
PrivateSub LBL_CLOSE_MouseEnter(sender AsObject, e As EventArgs)Handles LBL_CLOSE.MouseEnter
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#e57373"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(100, 0, 0, 0), New TransitionType_Linear(250)) EndSub
PrivateSub LBL_CLOSE_MouseLeave(sender AsObject, e As EventArgs)Handles LBL_CLOSE.MouseLeave
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(0, 0, 0, 0), New TransitionType_Linear(250)) EndSub
PrivateSub LBL_REDUCE_MouseEnter(sender AsObject, e As EventArgs)Handles LBL_REDUCE.MouseEnter
Transition.run(LBL_REDUCE, "ForeColor", ColorTranslator.FromHtml("#4fc3f7"), New TransitionType_Linear(250))
Transition.run(LBL_REDUCE, "BackColor", Color.FromArgb(100, 0, 0, 0), New TransitionType_Linear(250)) EndSub
PrivateSub LBL_REDUCE_MouseLeave(sender AsObject, e As EventArgs)Handles LBL_REDUCE.MouseLeave
Transition.run(LBL_REDUCE, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), New TransitionType_Linear(250))
Transition.run(LBL_REDUCE, "BackColor", Color.FromArgb(0, 0, 0, 0), New TransitionType_Linear(250)) EndSub
EndClass
Je ne sais pas vous, mais je trouve ce code assez désorganisé. Nous voulons écrire un code propre, donc nous allons voir comment nous organiser parmi les classes et les méthodes.
VII. Optionnel - l'organisation
Plus tôt dans ce tutoriel, je vous ai demandé de créer un dossier nommé « Core » dans l’arborescence des fichiers du projet.
Cela permet, au fur et à mesure que le nombre de fichiers dans le projet grandit, de rester organiser. En effet, plus le code va grandir, plus vous ajouterez de fichiers, de classes, etc. D’où le principe de les regrouper par catégorie.
Ici, le dossier Core contient tout ce qui est lié au fonctionnement interne du lanceur. Core veut dire « noyau », je ne sais pas si c’est la meilleure manière de nommer le dossier, mais elle reste assez parlante.
Par exemple, si le nombre de classe servant à gérer certains aspects du lanceur (managers), comme le FontManager, voient le jour dans ce projet, je vais certainement toutes les mettre dans un dossier Managers.
De même que nous trions l’arborescence d’un projet, les lignes de code à l’intérieur des fichiers de ce dernier doivent elle aussi être organisées.
a. Les régions
Le charabia que j’ai écrit plus haut étant un bel exemple de ce qu’il ne faut pas faire, je vous conseille donc de ranger ces lignes de code par catégories grâce au mot-clé #Region(avec le #).
Par exemple :
Code (vbnet):
#Region ’’Events’’ PrivateSub Main_Load() …
#Region “ControlBox” PrivateSub LBL_CLOSE_MouseEnter() … PrivateSub LBL_CLOSE_MouseLeave () … #End Region #End Region
C’est ainsi que nous allons nous organiser.
b. Les commentaires
Il faut aussi veiller à commenter certaines fonctions.
Que ce soit par esthétique du code ou par prévention de relecture, il faut s’habituer à commenter nos lignes de code. Particulièrement les fonctions : mettez trois apostrophes sur la ligne au-dessus d’une méthode et vous pourrez écrire à quoi elle sert.
Ce sera notamment utile lors du survol à la souris d’un appel à sa fonction pour voir ce que vous avez écrit, donc son fonctionnement, et à l’écriture de ses arguments pour voir le type et le but de ces arguments.
Figure 13 - Le commentaire prend trois lignes et est entre balises
Je vous invite donc à copier-coller le code ci-dessous (ou à le réorganiser vous-même) afin d’organiser notre charabia.
''' <summary> ''' Se déclenche lorsque la souris entre sur le bouton de fermeture. ''' </summary> PrivateSub LBL_CLOSE_MouseEnter(sender AsObject, e As EventArgs)Handles LBL_CLOSE.MouseEnter
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#e57373"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(100, 0, 0, 0), New TransitionType_Linear(250)) EndSub PrivateSub LBL_CLOSE_MouseLeave(sender AsObject, e As EventArgs)Handles LBL_CLOSE.MouseLeave
Transition.run(LBL_CLOSE, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), New TransitionType_Linear(250))
Transition.run(LBL_CLOSE, "BackColor", Color.FromArgb(0, 0, 0, 0), New TransitionType_Linear(250)) EndSub
''' <summary> ''' Se déclenche lorsque la souris entre sur le bouton de réduction. ''' </summary> PrivateSub LBL_REDUCE_MouseEnter(sender AsObject, e As EventArgs)Handles LBL_REDUCE.MouseEnter
Transition.run(LBL_REDUCE, "ForeColor", ColorTranslator.FromHtml("#4fc3f7"), New TransitionType_Linear(250))
Transition.run(LBL_REDUCE, "BackColor", Color.FromArgb(100, 0, 0, 0), New TransitionType_Linear(250)) EndSub PrivateSub LBL_REDUCE_MouseLeave(sender AsObject, e As EventArgs)Handles LBL_REDUCE.MouseLeave
Transition.run(LBL_REDUCE, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), New TransitionType_Linear(250))
Transition.run(LBL_REDUCE, "BackColor", Color.FromArgb(0, 0, 0, 0), New TransitionType_Linear(250)) EndSub
À noter › Vous avez sans doute remarqué l’espèce de grand commentaire sur trois lignes. Je n’aime pas les régions imbriquées, j’ai donc choisi ce commentaire pour séparer mes évènements dans la région correspondante.
VII. Finitions de la Control Box
a. Fermeture et réduction
Notre Control Box est certes, jolie, mais pour le moment non-fonctionnelle : nous allons remédier à cela. Ouvrez l’évènement MouseClick des deux étiquettes en double-cliquant dessus.
Dans l’évènement du bouton de fermeture, insérez ce code : Me.Close. Il permet de fermer la fenêtre.
Dans l’évènement du bouton de réduction, insérez celui-ci : Me.WindowState = FormWindowState.Minimized. Il permet de réduire notre fenêtre.
b. Animations
Tout cela est bien joli, mais justement, pas assez (oui, c’est moi qui ai dit que je ne m’attarderai pas sur les graphismes… chut.).
Nous allons animer la fermeture de notre fenêtre par un joli fondu. Ouvrez l’évènement FormClosing de votre Main.
Figure 14 - Ouverture de l'évènement FormClosing
Malheureusement, il n’est pas possible de modifier l’opacité de la fenêtre à l’aide de la bibliothèque Transtion Dot Net. Nous allons donc devoir passer par un autre moyen.
Créez un dossier « Miscellaneous » et déplacez-y la classe LauncherVariables. Créez maintenant une classe LauncherMethods. Nous allons mettre dedans cette classe des méthodes globales, utiles un peu partout.
Vous pouvez y coller ce code :
Code (vbnet):
PublicClass LauncherMethods
#Region " Fondu "
PublicEnum FadeAction
Show
Hide EndEnum PublicEnum FadeTime
Slow =500
Medium =250
Fast =185 EndEnum
''' <summary> ''' Permet d'afficher ou de cacher une fenêtre en fondu. ''' </summary> PublicSharedSub FadeWindow(Target As Form, ByVal Action As FadeAction, OptionalByVal FadeTime As FadeTime =500, OptionalByVal OPMAX AsInteger=100) Try SelectCase Action Case FadeAction.Show
Target.Opacity=0
Target.Show() Dim Inc AsInteger Dim checkT AsInteger= My.Computer.Clock.TickCount DoWhile Inc < FadeTime
Inc = My.Computer.Clock.TickCount- checkT
Target.Opacity= Inc / FadeTime If Target.Opacity>= OPMAX ThenExitSub
Application.DoEvents() Loop Case FadeAction.Hide If Target.Opacity=0ThenExitSub Dim Inc AsInteger Dim checkT AsInteger= My.Computer.Clock.TickCount DoWhile Inc < FadeTime
Inc = My.Computer.Clock.TickCount- checkT
Target.Opacity=1-1/(FadeTime / Inc)
Application.DoEvents() Loop EndSelect
Catch ex As Exception
Logger.Exception(ex) EndTry EndSub
Vous pouvez maintenant mettre, à l’intérieur de l’évènement FormClosing, cet appel : LauncherMethods.FadeWindow(Me, FadeAction.Hide, FadeTime.Fast).
Attention › N’oubliez pas d’importer LauncherMethods en début de fichier !
Si vous démarrez votre lanceur et que vous le fermez à l’aide du bouton, il devrait disparaître en un léger fondu.
À noter › Les clignotements (flickers en anglais) qui apparaissent sont dûs aux WinForms, plus particulièrement à l’image de fond. Il n’est malheureusement pas possible de les faire disparaître totalement. Vous pouvez vous renseigner sur WPF qui est fait pour ça, mais je vais continuer ce tutoriel en WinForms.
Le mieux que l’on puisse faire pour faire disparaître ces clignotements est de rajouter cette ligne dans l’évènement Load du Main : Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True). Mais au fur et à mesure que le lanceur s’alourdira, les clignotements peuvent revenir.
Maintenant, vous pouvez ajouter ce même appel dans l’évènement Load afin d’ouvrir le lanceur en fondu. N’oubliez pas de remplacer FadeAction.Hide par FadeAction.Show, sinon, ce ne sera pas joli.
IV. Les graphismes (deuxième partie)
Nous allons continuer la création des graphismes du lanceur. Tout d’abord, commencez par réorganiser le code : nous avons trop de lignes dans l’évènement Load, nous allons donc créer une fonction d’initialisation.
Commencez par créer une nouvelle région « Méthodes » au-dessus de la région « Évènements ». Cela fait, créez une partie « Formulaire » à l’aide de commentaires, puis créez une fonction « Initialize » avec les lignes de l’évènement Load, mais commentées. Le tout dans un bloc Try/Catch.
Try ' Prévention des scintillements Me.SetStyle(ControlStyles.AllPaintingInWmPaintOr ControlStyles.UserPaintOr ControlStyles.DoubleBuffer, True)
' Affichage en fondu
LauncherMethods.FadeWindow(Me, FadeAction.Show, FadeTime.Fast)
' Modification de la control box Me.PNL_TOP.Font= FontManager.GetFont(My.Resources.MaterialIcons_Regular, 15) Me.LBL_CLOSE.Font= PNL_TOP.Font Me.LBL_CLOSE.Text=ChrW(&HE5CD) Me.LBL_REDUCE.Font= PNL_TOP.Font Me.LBL_REDUCE.Text=ChrW(&HE5CF) Catch ex As Exception
Logger.Exception(ex) EndTry
Bien. Une bonne chose de faite. Pour être sûrs d’être au même niveau, voici le code complet actuel.
a. Le titre et le logo de la fenêtre
Nous avons créé la Control Box, mais puisque nous avons enlevé la barre par défaut, le titre a lui aussi disparu. Le logo aussi. Je vous propose d’aller choisir un logo pour votre lanceur. Il y a plusieurs options :
1. Vous avez déjà un logo
Vous pouvez le convertir si ce n’est déjà fait sur le site ConvertIco.
Attention › Un logo de taille 32*32 pixels est très fortement conseillé, si vous réduisez votre logo de 1000*1000 à 32*32, il va être pixelisé et ne rendra pas bien.
Évitez aussi les ombres, le biseautage, tout ce qui peut faire du relief. Préférez un logo plat.
2. Vous n’avez pas de logo
Vous pouvez aller en trouver un sur IconFinder ou prendre un logo Material si vraiment vous n’avez pas d’inspiration. Vous pouvez aussi reprendre le logo de base de Minecraft.
Pour ma part, j’ai pris un logo sur IconFinder, que j’ai converti en icône sur ConvertIco, et j’ai ajouté des marges au fichier original sur Paint .NET. J’suis un mec polyvalent, moi.
Figure 15 - Le logo que j'ai choisi pour mon lanceur
Importez votre logo en .PNG dans les ressources de votre lanceur, et créez une PictureBox à l’intérieur du PNL_TOP, de la même manière que vos étiquettes de Control Box, mais à gauche :
BackColor à Transparent
BackgroundImage à votre image dans les ressources
BackgroundImageLayout à Zoom
Name à PBX_LOGO – ceci est important
Dock à Left
Size à 38; 38
Vous avez maintenant un joli logo. Mettez aussi l'image en .ICO dans la propriété Icon de votre Main.
On va maintenant ajouter une étiquette qui sera le titre :
À noter › Mettre une aussi grande largeur est simplement une mesure de précaution. Vous pouvez bien évidemment la réduire.
Rendez-vous dans la fonction Initialize et ajoutez cette ligne : Me.LBL_TITLE.Text = Me.Text.
Lancez en appuyant sur F5, et… c’est l’échec ! Si vous avez bien suivi ces étapes, vous ne voyez que la première lettre de votre titre.
Pour faire face à ce problème, nous allons modifier le code avec lequel nous récupérons les polices d’écriture.
Ajoutez trois fonctions publiques et partagées, avec comme argument optionnel la taille de la police et comme valeur de retour chaque police d’écriture, dans LauncherVariables : une pour les icônes, les deux autres pour les Roboto que nous allons importer.
Importez les polices Roboto Regular et Roboto Medium, téléchargées précédemment, dans votre projet.
Vous pouvez maintenant modifier votre fonction Initialize telle que suit :
Code (vbnet):
PrivateSub Initialize()
Try ' Prévention des scintillements Me.SetStyle(ControlStyles.AllPaintingInWmPaint OrControlStyles.UserPaint OrControlStyles.DoubleBuffer, True)
' Modification de la control box Me.LBL_CLOSE.Font= LauncherVariables.MATERIAL_ICONS Me.LBL_CLOSE.Text=ChrW(&HE5CD) Me.LBL_REDUCE.Font= LauncherVariables.MATERIAL_ICONS Me.LBL_REDUCE.Text=ChrW(&HE5CF)
' Modification du label titre Me.LBL_TITLE.Font= LauncherVariables.ROBOTO_REGULAR(12) Me.LBL_TITLE.Text=Me.Text
' Affichage en fondu
LauncherMethods.FadeWindow(Me, FadeAction.Show, FadeTime.Fast)
Catch ex As Exception
Logger.Exception(ex) EndTry EndSub
Au cas où, voici le LauncherVariables actuel (auquel j'ai d'ailleurs ajouté une constante pour la future taille de la bordure) :
PublicSharedFunction MATERIAL_ICONS(OptionalByVal Size AsSingle=15)As Font Return FontManager.GetFont(My.Resources.MaterialIcons_Regular, Size)
EndFunction
PublicSharedFunction ROBOTO_REGULAR(OptionalByVal Size AsSingle =12)As Font Return FontManager.GetFont(My.Resources.Roboto_Regular, Size)
EndFunction
PublicSharedFunction ROBOTO_MEDIUM(OptionalByVal Size AsSingle =12)As Font Return FontManager.GetFont(My.Resources.Roboto_Medium, Size)
EndFunction
EndClass
Vous lancez votre programme avec F5, et là… le texte s'affiche, mais pixellisé. Le souci, ici, c’est l’anti-crénelage (anti-aliasing en anglais). Par défaut, on ne peut pas modifier la qualité de rendu d’un label. Je vais donc créer un label personnalisé, vous n’êtes pas obligés de le faire.
b. Optionnel - le label personnalisé
Créez un nouveau dossier Custom Controls et ajoutez-y une class SmoothLabel. Je suis allé piocher un morceau de code assez simple mais plutôt pratique sur Stackoverflow. Pour pouvoir l’utiliser, votre classe doit être partielle, importer System.Drawing.Text et doit être héritée de Label (l’héritage est un concept de la POO, si vous ne savez toujours pas ce que c'est, je vous conseille... le tutoriel donné en début de tutoriel :3).
Code (vbnet):
Imports System.Drawing.Text
PartialPublicClass SmoothLabel
Inherits Label
Private _Hint As TextRenderingHint = TextRenderingHint.SystemDefault PublicProperty TextRenderingHint() AsTextRenderingHint Get ReturnMe._Hint
EndGet Set(value As TextRenderingHint) Me._Hint = value
EndSet
EndProperty
ProtectedOverridesSub OnPaint(pe As PaintEventArgs)
pe.Graphics.TextRenderingHint= TextRenderingHint MyBase.OnPaint(pe)
EndSub
Pour ajouter le contrôle, lancez le programme avec F5 et fermez-le.
Vous pouvez maintenant supprimer l’étiquette LBL_TITLE et la recréer, mais avec le contrôle SmoothLabel, fraichement ajouté à la boite à outils.
Paramétrez l’étiquette comme précédemment, et vérifiez que le paramètre TextRenderingHint est bien à AntiAlias. Lancez votre programme : le titre est exactement comme il faut !
c. Des contours
Attaquons-nous aux contours. Ils sont assez important esthétiquement, et assez faciles à dessiner. Il nous faut utiliser GDI+ : nous allons réécrire la fonction OnPaintBackground afin de dessiner quelque chose.
Je vous laisse ajouter cette fonction, facile à comprendre :
Code (vbnet):
''' <summary> ''' Se déclenche lors que le fond doit être paint. ''' </summary> ProtectedOverridesSub OnPaintBackground(ByVal e As PaintEventArgs) MyBase.OnPaintBackground(e)
' Ligne de gauche
e.Graphics.FillRectangle(New SolidBrush(Me.PNL_TOP.BackColor), New Rectangle( New Point(0, Me.PNL_TOP.Height), New Size(LauncherVariables.BORDER_SIZE, (Me.Height-Me.PNL_TOP.Height))))
' Ligne de droite
e.Graphics.FillRectangle(New SolidBrush(Me.PNL_TOP.BackColor), New Rectangle( New Point(Me.Width- LauncherVariables.BORDER_SIZE, Me.PNL_TOP.Height), New Size(LauncherVariables.BORDER_SIZE, (Me.Height-Me.PNL_TOP.Height))))
' Ligne du bas
e.Graphics.FillRectangle(New SolidBrush(Me.PNL_TOP.BackColor), New Rectangle( New Point(LauncherVariables.BORDER_SIZE, (Me.Height- LauncherVariables.BORDER_SIZE)), New Size(Me.Width-(2* LauncherVariables.BORDER_SIZE), LauncherVariables.BORDER_SIZE))) EndSub
Vous pouvez voir le résultat en appuyant sur F5.
d. Déplacement de la fenêtre
Vous avez sûrement dû remarquer qu’on ne pouvait pas bouger la fenêtre : cela est dû au fait que nous avons enlevé les bordures par défaut, pour ajouter les nôtres.
Nous allons donc écrire notre propre gestionnaire de déplacement. Créez un nouveau dossier « Managers » et déplacez-y FontManager. Créez ensuite « MoveManager ».
Nous devons créer une instance de MoveManager à chaque fois qu’une nouvelle fenêtre sans bordure s’ouvre. Il lui faut donc un constructeur.
Ce constructeur devra enregistrer une référence vers la fenêtrequi l’appelle ainsi qu’une liste de contrôle sur lesquels nos clics déplaceront la fenêtre. Il faut donc une variable privée ainsi qu’une boucle dans laquelle nous ajouterons des gestionnaires pour les évènements MouseDown et MouseMove :
Code (vbnet):
''' <summary> ''' Instanciation du MoveManager, lié à l'objet LinkedForm ''' </summary> ''' <param name="LinkedForm">La fenêtre à laquelle lier le MoveManager</param> ''' <remarks></remarks> PublicSubNew(ByRef LinkedForm As Form, ByRef MoveableControls As List(OfControl)) Me._MouseOffset = NewPoint(0, 0) Me._LinkedForm = LinkedForm
ForEach MoveableControl AsControl In MoveableControls AddHandler MoveableControl.MouseDown, AddressOf _LinkedForm_MouseDown AddHandler MoveableControl.MouseMove, AddressOf _LinkedForm_MouseMove Next EndSub
Les évènements MouseDown et MouseMove devront permettre de modifier la position de la fenêtre en fonction de notre clic de souris : MouseDown permettre d’enregistrer un offset (une distance de compensation) entre la position du clic et la position de la fenêtre, et le MouseMove déplacera la fenêtre en fonction de la position de la souris et de cet offset.
Code (vbnet):
''' <summary> ''' Se déclenche lorsque l'utilisateur maintient un clic sur un contrôle listé ''' </summary> PrivateSub _LinkedForm_MouseDown(ByVal sender AsObject, ByVal e As MouseEventArgs)
_MouseOffset =New Point(-(e.X+ sender.Location.X), -(e.Y+ sender.Location.Y))
EndSub
''' <summary> ''' Se déclenche lorsque l'utilisateur déplace sa souris ''' </summary> PrivateSub _LinkedForm_MouseMove(ByVal sender AsObject, ByVal e As MouseEventArgs) If e.Button= MouseButtons.LeftThen Dim MousePosition As Point = Control.MousePosition
MousePosition.Offset(_MouseOffset.X, _MouseOffset.Y)
_LinkedForm.Location= MousePosition EndIf EndSub
Il nous reste à créer une instance de MoveManager au démarrage du lanceur. Ajoutez cette ligne dans la fonction Initiate : Dim MoveManager As NewMoveManager(Me, New List(Of Control)(New Control() {PNL_TOP, LBL_TITLE, PBX_LOGO})).
Ce qui se trouve entre crochets sont les noms des contrôles que nous autorisons à utiliser pour le déplacement. Si vous avez bien compris, vous savez que vous pourrez désormais déplacer la fenêtre grâce au cadre noir en haut, au logo ainsi qu’au titre de la fenêtre, mais pas grâce à la Control Box.
X. L’authentification
Nous sommes des gens bien. Les cracks, c’est pas bien. On ne va pas autoriser les cracks. On pourrait. Mais on ne va pas le faire.
a. Préparation
On va déjà commencer par créer les contrôles nécessaires, soit deux zones de texte, une pour le pseudonyme (ou adresse mail), une pour le mot de passe, et un bouton de validation.
Commencez par ajouter un Panel à votre Main. Modifiez son nom en PNL_LOGIN, son BackColor en 153 ; 0 ; 0 ; 0, son Size en 300 ; 300, et son Location en 84; 172 – ce dernier paramètre est pour centrer verticalement le panel et le rapprocher du bord gauche.
Changez également son ForeColor à White.
1. Les zones de texte
À noter › Je vais expliquer ici comment utiliser une zone de texte transparente. Vous n’êtes pas obligés de suivre ces étapes, et pouvez utiliser une zone de texte normale.
Nativement, il n’est pas possible de rendre transparente une zone de texte. Mais un gentilhomme nous a fourni une bibliothèque disponible ici, qui permet d’intégrer une zone transparente.
Pour l’installer, cliquez-droit sur Tous les Windows Forms dans la boîte à outils, cliquez sur Choisir les éléments, puis Parcourir, et enfin, sélectionnez AlphaBlendTextBox.dll là où vous l’avez extrait. Validez en appuyant sur OK, et ajoutez le contrôle qui vient d’apparaître : vous venez d’intégrer une zone de texte transparente.
AlphaBlendTextBox est apparu dans la boîte à outils
Pour les autres, vous pouvez simplement prendre une zone de texte normale. Dans les deux cas, appelez cette zone de texte TXT_USERNAME.
Créez un nouveau Panel et insérez-le dans PNL_LOGIN. Voici ses propriétés :
Name : PNL_LOGIN_USERNAME
BackColor : 153 ; 0 ; 0 ; 0
Location : 30; 182
Size : 240; 30
Cursor : IBeam
Maintenant, placez votre TXT_USERNAME dans PNL_LOGIN_USERNAME et centrez-la (location : 8 ; 9, et size : 225 ; 13). Cette astuce de type barbare permet de donner l’impression d’avoir une grande zone de texte.
Faites pareil pour la zone du mot de passe. Le Panel doit se nommer PNL_LOGIN_PASSWORD et se trouver en 30 ; 218. Sa zone de texte doit, elle, avoir le nom TXT_PASSWORD, et la valeur PasswordChar à « × ».
Placez maintenant le code suivant dans la zone destinées aux évènements du Login Panel :
Code (vbnet):
''' <summary> ''' Se déclenche lorsque l'utilisateur clique sur un panel. ''' </summary> PrivateSub PNL_LOGIN_USERNAME_MouseClick(sender AsObject, e As MouseEventArgs)Handles PNL_LOGIN_USERNAME.MouseClick
TXT_USERNAME.Focus() EndSub PrivateSub PNL_LOGIN_PASSWORD_MouseClick(sender AsObject, e As MouseEventArgs)Handles PNL_LOGIN_PASSWORD.MouseClick
TXT_PASSWORD.Focus() EndSub
Ce petit bout de code permettre de mettre le focus sur la zone de texte correspondant à chaque Panel, afin que l'impression de grandeur desdites zones soit totale.
Malheureusement, il est plus compliqué de changer la qualité du texte d’une zone de texte, je ne vais donc pas le voir ici. À la place, je vais mettre la police d’écriture « Segoe UI » pour les deux zones de texte.
2. Le bouton de connexion
Certains d’entre vous vont trouver la méthode qui suit barbare, et ils auront raison. Mais c’est le meilleur moyen que j’ai trouvé de faire ce que je voulais (sans passer par GDI+, évidemment).
Sachez que vous feriez mieux de modifier le fond directement en utilisant GDI+, mais par simplicité, j’ai choisi ce qui suit :
Créez un SmoothLabel avec les propriétés suivantes :
BackColor : 63 ; 0 ; 0 ; 0 (25% noir)
Cursor : Hand
Font : Roboto ; 9,25pt
ForeColor : 236, 240, 241
Text : Connexion
TextAlign : MiddleCenter
Name : BTN_LOGIN
Location : 86;255
Size : 129;31
TextRenderingHint : AntiAlias
Ce label nous servira de bouton de connexion. Je n’ai pas utilisé de bouton, car il serait plus compliqué de modifier son anti-crénelage, alors que notre SmoothLabel rend exactement comme si c'était un bouton. Autant donc utiliser notre SmoothLabel.
Créez un nouveau Label (ou SmoothLabel, peu importe) avec les propriétés suivantes :
BackColor : 41 ; 128 ; 185
Font : Roboto ;9,25pt
ForeColor : White
Text : vide
Name : LBL_LOGIN_UNDERLINE
Location : 86; 284
Size : 129; 2
Vous comprenez ? Cette étiquette va nous servir de bordure inférieure.
Pour rendre le tout sympathique, ajoutez ceci sous le code déjà présent de la control box :
''' <summary> ''' Se déclenche lorsque la souris entre sur le bouton de connexion. ''' </summary> PrivateSub BTN_LOGIN_MouseEnter(sender AsObject, e As EventArgs)Handles BTN_LOGIN.MouseEnter
Transition.run(BTN_LOGIN, "ForeColor", ColorTranslator.FromHtml("#ecf0f1"), NewTransitionType_Linear(250))
Transition.run(BTN_LOGIN, "BackColor", Color.FromArgb(154, 0, 0, 0), New TransitionType_Linear(350))
Transition.run(LBL_LOGIN_UNDERLINE, "BackColor", Color.FromArgb(154, 41, 128, 185), New TransitionType_Linear(500)) EndSub PrivateSub BTN_LOGIN_MouseLeave(sender AsObject, e As EventArgs)Handles BTN_LOGIN.MouseLeave
Transition.run(BTN_LOGIN, "ForeColor", Color.White, New TransitionType_Linear(250))
Transition.run(BTN_LOGIN, "BackColor", Color.FromArgb(63, 0, 0, 0), New TransitionType_Linear(350))
Transition.run(LBL_LOGIN_UNDERLINE, "BackColor", Color.FromArgb(255, 41, 128, 185), New TransitionType_Linear(500)) EndSub
Ouvrez maintenant votre lanceur, et regardez le résultat. Joli, n’est-ce pas ?
Je vais également ajouter deux étiquettes de soulignage sous mes zones de texte. Vous n’êtes pas obligés de le faire, mais si vous le faites, elles auront la même couleur que celle sous le bouton, mais leurs couleurs changeront lors du changement de focus de la zone de texte correspondante :
Code (vbnet):
''' <summary> ''' Se déclenche lorsque la focus entre ou sort du mot de passe. ''' </summary> PrivateSub TXT_PASSWORD_Enter(sender AsObject, e As EventArgs)Handles TXT_PASSWORD.Enter
Transition.run(LBL_PASSWORD_UNDERLINE, "BackColor", Color.FromArgb(154, 41, 128, 185), New TransitionType_Linear(500)) EndSub
PrivateSub TXT_PASSWORD_Leave(sender AsObject, e As EventArgs)Handles TXT_PASSWORD.Leave
Transition.run(LBL_PASSWORD_UNDERLINE, "BackColor", Color.FromArgb(255, 41, 128, 185), New TransitionType_Linear(500)) EndSub
De même pour la zone du nom d’utilisateur.
Maintenant que les préparations graphiques sont faites, nous allons passer au script d’authentification.
Figure 16 - État actuel du lanceur, un peu vide dans son ensemble
b. Utilisation de MojangAPI MojangAPI est une bibliothèque de classes que j'ai développée dans le cadre de ce tutoriel, afin de faciliter le processus d'authentification auprès des serveurs de Mojang, et en prévention des futures requêtes que nous allons effectuer sur ces mêmes serveurs, notamment pour la récupération du skin.
Je vous invite à télécharger cette bibliothèque sur sa page GitHub (bouton Télécharger en haut de l'arborescence). Notez qu'elle est open-source, vous pouvez donc voir l'intérieur de la classe.
Figure 17 - Téléchargement de MojangAPI
Vous n'avez plus qu'à intégrer cette bibliothèque à votre projet.
Rappel › pour ajouter une bibliothèque, rendez-vous dans les propriétés du projet (My Project), puis dans références, cliquez sur Ajouter, puis Parcourir..., et trouvez la bibliothèque.
J'ai simplifié au maximum l'utilisation de cet API afin d'avoir le moins de lignes possible à écrire.
Je vais expliquer ici comment lancer une requête d'authentification ; si vous voulez plus de détails, jetez un œil dans le readme.md de la page GitHub (en anglais).
Vous devez tout d'abord déclarer un objet Request, situé dans l'espace de noms MojangAPI :
Code (vbnet):
Dim AuthRequest AsNew Request(Request.Method.POST, URL.AUTHENTICATE)
AuthRequest étant le nom de la variable de type Request, et les deux arguments étant respectivement le type de requête http (ici, POST) et l'adresse de la requête (ici, l'adresse du serveur d'authentification avec l'endpoint /Authenticate).
À noter › plusieurs adresses sont disponibles dans la classe URL, notamment le Refresh, Validate, Invalidate et Signout du serveur d'authentification.
La requête étant maintenant créée, il va falloir l'exécuter :
Code (vbnet):
Dim AuthResponse AsNew AuthenticationResponse(
AuthRequest.Execute(
Headers.Authenticate(TXT_USERNAME.Text, TXT_PASSWORD.Text)))
Nous avons ici une nouvelle variable AuthResponse de type AuthenticationResponse, qui contient comme paramètre un texte JSON obtenu grâce à la méthode Execute de notre type Request, qui lui-même possède comme argument le Header nécessaire aux requêtes de type POST, qui lui-même contient comme argument le nom d'utilisateur et le mot de passe rentrés dans nos zones de texte.
À noter › ceci est juste une explication, nous allons créer un thread pour éviter de geler la fenêtre. De plus, nous devons vérifier le contenu des zones de texte avant d'envoyer la requête.
Le code complet donnerait quelque chose comme ça :
Code (vbnet):
Dim AuthRequest AsNew Request(Request.Method.POST, URL.AUTHENTICATE) Dim AuthResponseAs New AuthenticationResponse(AuthRequest.Execute(Headers.Authenticate(TXT_USERNAME.Text, TXT_PASSWORD.Text)))
If AuthResponseAs .GetResponse.Error=NothingThen' On vérifie si une erreur est survenue ' Si non, nous avons accès aux tokens et au pseudo de l'utilisateur
Console.WriteLine("AccessToken: "& AuthenticationResponse.GetResponse.AccessToken)
Console.WriteLine("ClientToken: "& AuthenticationResponse.GetResponse.ClientToken)
Console.WriteLine("UserName: "& AuthenticationResponse.GetResponse.UserName)' Le nom d'utilisateur du joueur. Ce nom n'est pas forcément le contenu de TXT_USERNAME, ce dernier pouvant être une adresse mail Else
Console.WriteLine("Error: "& AuthenticationResponse.GetResponse.ErrorMessage)' Généralement, un nom d'utilisateur ou mot de passe erroné EndIf
Attention › ne copiez pas ce code. Je l'ai mis dans un but explicatif ; de toute manière, il ne fonctionnera pas sur votre lanceur car il n'y a pas de console visible.
c. AuthManager et threads [non fonctionnel]
C'est ici que les choses vont se compliquer.
Si vous avez effectué des tests avec MojangAPI, vous aurez sans doute remarqué qu'une requête est bloquante, car je n'ai pas intégré de multi-threading dans cette bibliothèque de classes.
Nous allons donc devoir utiliser notre propre système de multi-threading, afin d'éviter de bloquer l'interface utilisateur lors d'une requête.
À noter › si nous effectuons des actions « bloquantes » sur un thread, nous devrons attendre que l'opération soit terminée pour interagir à nouveau avec ce thread. Créer un autre thread permet d'effectuer des actions bloquantes sur ce dernier et de continuer à interagir avec le premier (ici, l'interface).
1. Création de la classe AuthManager
C'est une classe qui va gérer les authentifications. Nous allons définir un évènement AuthRequestFinished avec comme paramètre un objet AuthentificationResponse (de MojangAPI).
[En cours d'écriture]
Le tutoriel est toujours en cours de rédaction.
Je passe beaucoup de temps sur sa rédaction, un commentaire ou une appréciation serait la bienvenue :)
Il ne sera pas finit de si tôt, mais j'avance le plus vite possible. J'attends vos commentaires ! Merci d'avoir lu. :) « guillemets »