I. Avant-propos▲
Ce document a pour but de vous montrer comment, avec Visual Basic versions 4.0 à 6.0, concevoir des formulaires de différentes formes, qu'elles soient géométriques ou tracées à main levée, voire dotées de trous…
L'exploitation de ce tutoriel vous ouvrira de nombreuses possibilités pour réaliser des fenêtres de formulaires hors du commun.
En effet, lorsque vous créez un nouvel objet Form, celui-ci est une simple fenêtre rectangulaire dotée ou non d'une barre de titre, mais restera de toute façon à quatre coins.
Grâce à ce tutoriel, vous serez en mesure de concevoir des formulaires polyformes.
I-A. Remerciements▲
Je tiens à remercier tout particulièrement toutes celles et tous ceux qui ont participé à la relecture de ce document en y incluant leurs remarques.
I-B. Contact▲
Pour tout renseignement complémentaire, veuillez me contacter directement (Argyronet) par MP.
II. Présentation du projet▲
Pour illustrer ce tutoriel et vous donner envie de le lire dans sa totalité, je vous mets d'entrée 4 idées représentatives de ce que va pouvoir vous apporter la lecture de ce document.
En effet, pour mes projets de réalisations multimédias sous Visual Basic, je me suis penché sur la possibilité de sortir de l'ordinaire en matière de conception de formulaires entre guillemets rigolos…
Cela était possible… En C++ ; Je me suis dit qu'en exploitant les mêmes API sous Visual Basic, on devrait pouvoir satisfaire cette possibilité ; ce fut le cas :
Un formulaire sous forme de feuille
Un formulaire sous forme d'étoile trouée
Un formulaire sous forme de calculatrice1
(1) - Calculatrice pédagogique animée et parlante développée pour les enfants
Un formulaire sous forme d'Horloge Coucou2
(2) - Horloge pédagogique animée et parlante développée pour les enfants
Comme vous pouvez le constater, nous pouvons réaliser à peu près n'importe quelle forme de formulaire pour peu que l'on respecte deux éléments essentiels :
- faire en sorte que l'image possède un contour uniforme et monochrome suffisamment discret pour que cela ne se remarque pas ;
- faire en sorte que l'arrière-plan soit défini avec une couleur RVB uniforme absolument absente de l'image dessinée ;
en effet, le principe de mise en exécution de ces formulaires se base sur le fait qu'une couleur (celle de l'arrière plan) soit mise en transparence grâce à une combinaison de fonctions API Windows spécifiques.
III. Conception d'un formulaire déformé▲
Avant toute chose, je précise ici que je ne m'étendrai pas sur les explications concernant à l'utilisation même de Visual Basic et en particulier la création de formulaires… Vous devez être en mesure de comprendre ce qu'est l'objet formulaire et toutes les propriétés qui le composent ainsi que de connaître les événements dont il est pourvu.
Si vous ignorez comment concevoir un formulaire, je vous conseille de vous rendre à cette page et de lire par exemple ce tutoriel…
III-A. Création du formulaire▲
Pour concevoir la forme de ce formulaire, j'utiliserai le logiciel de dessin MsPaint® livré par défaut avec Windows.
Créez d'abord un nouveau formulaire depuis la boîte de dialogue Project/Add Form… et sélectionnez la première icône de la liste nommée Form :
Vous allez alors obtenir une fenêtre vide qui portera le nom de Form suivi d'un numéro correspondant à l'index du nombre de fenêtres que vous avez déjà créées (Exemple ici Form2).
Une fois cela fait, cliquez sur le bouton Démarrer de Windows où vous choisirez Exécutez…
Pour les adeptes des raccourcis, appuyez simultanément sur les touches …
La boîte de dialogue apparaît et vous y inscrivez le nom du programme à savoir, Mspaint…
Si vous préférez utiliser un autre logiciel graphique, il n'y a aucune difficulté du moment que vous respectez les indications qui vont suivre.
Je précise que l'usage de MsPaint permet de générer des images Bitmap simples et c'est ce qu'il y a de plus conseillé pour débuter.
Vous cliquez alors sur le bouton OK.
Le logiciel vous affiche une feuille blanche sur laquelle vous allez dessiner une forme géométrique simple.
Pour illustrer cet exercice, je vous propose de dessiner une étoile.
Petite astuce
Pour dessiner une étoile, utilisez l'outil Polygone et formez un triangle isocèle que vous dupliquez cinq fois en utilisant deux instances de MsPaint et en utilisant l'outil de rotation de MsPaint…
Vous effectuez alors un copier-coller à chaque fois et nettoyez le surplus de pixels dessinés.
Dessin de l'étoile dans MsPaint
Faites très attention à une chose importante…
Il est impératif de vérifier chaque extrémité lorsque la forme dessinée est complexe de manière à s'assurer qu'il n'existe aucun interstice représenté par un pixel vide.
En effet, au moment du remplissage et en particulier avec MsPaint®, le fait de laisser un pixel vide autorisera la séquence de remplissage à recouvrir la totalité du dessin.
Une fois votre forme géométrique dessinée, vous pouvez alors choisir une couleur de remplissage.
Du fait que la palette de MsPaint® ne propose qu'un jeu de 28 couleurs, vous cliquerez deux fois sur l'une des zones colorées de manière à choisir une couleur personnalisée.
Ici, j'ai choisi une couleur gris turquoise :
R=206 V=224 B=232;
donc, ici, vous saisissez directement ces valeurs dans les zones respectives.
Vous validez alors par OK puis prenez l'outil Pot de peinture nommé Remplissage pour appliquer la couleur dans l'étoile.
De la même manière, vous appliquerez le remplissage à l'extérieur de l'étoile avec une couleur RVB simple par exemple un bleu uni :
R=000 V=000 B=255;
La figure devient alors la suivante :
Il ne vous reste plus qu'à ajuster l'image de manière à ce que l'espace de remplissage inutilement utilisé soit rogné…
Vous enregistrez alors votre image au format BMP dans le dossier .\images (sous-dossier du dossier de votre application) et le nommez par exemple imagestar.bmp.
Vous basculez alors de nouveau sous Visual Basic où nous attend patiemment le Form2 laissé à l'abandon pendant tout ce travail…
III-B. Affectation de l'image au formulaire▲
L'étape suivante va consister à mettre en œuvre le formulaire. Dans les propriétés de ce dernier, repérez la propriété Picture et cliquez sur le bouton afin d'aller chercher l'image que vous venez de dessiner…
Ajustez alors le formulaire à une taille un peu plus grande que l'image.
Un certain nombre d'autres propriétés sont à définir pour ce formulaire et elles sont les suivantes :
- Name = frmTransStar
- BorderStyle = 0
- ScaleMode = 3 (Pixel)
- ShowInTaskbar = True
Pour continuer, vous allez prendre dans la boîte à outils, un contrôle PictureBox que vous allez positionner sur la droite de manière à ce qu'il ne vous gêne pas lors de la pose d'éventuels autres contrôles servant au formulaire à proprement parler.
Dessinez alors un petit carré d'environ 1 cm de coté (soit en gros 700 pixels) avec le curseur (figure 1), lâchez alors le bouton de la souris et le petit carré est entouré de 8 points d'ancrage (figure 2) ;
Figure 1 |
Figure 2 |
---|---|
|
|
Dans la fenêtre des propriétés, repérez la propriété AutoSize et affectez-lui True (figure 3).
Repérez ensuite la propriété Picture de ce contrôle et affectez-lui la même image que celle que vous avez affectée à la même propriété pour le formulaire à savoir imagestar.bmp. Un certain nombre d'autres propriétés sont à définir pour ce contrôle et elles sont les suivantes :
- Name = pctStar
- AutoRedraw = True
- AutoSize = True
- BorderStyle = 0
- ScaleMode = 3 (Pixel)
- Visible = False
Si entretemps, la taille de votre image change (vous l'avez redessinée, agrandie ou diminuée pour x raisons) il vous faut réajuster les dimensions du contrôle PictureBox de manière à ce que le bord droit et le bord bas effleurent exactement ceux de l'image insérée…
Sinon, ne changez rien…
Pourquoi devez-vous faire cela ? Tout simplement parce que le formulaire sera ajusté sur la taille de ce contrôle. Une fois cela fait, vous pouvez placer le contrôle PictureBox à peu près n'importe où sur le formulaire. Mettez-le là où il ne vous gêne pas pour travailler si par exemple, vous avez d'autres contrôles à ajouter à votre formulaire.
Prenez bien en considération que la taille maximum de votre formulaire sera celle du contrôle PictureBox.
Dans le menu Format, cliquez sur le menu Lock controls. Ainsi, vous ne pouvez pas faire de fausses manipulations quant à la modification des éléments.
En cas de besoin, déverrouillez cette propriété.
IV. Écriture du code du projet▲
Il est bien entendu évident que tout ce vous avez effectué jusqu'ici ne suffit pas à obtenir un formulaire en forme d'étoile…
Tout le principe de transformation du formulaire est fondé sur la destruction des zones de couleurs bien précises et ce à l'aide de fonctions spécifiques stipulées ci-après…
IV-A. Écriture du module▲
Insérez alors un nouveau module dans votre projet et profitez de ce que vous avez la main sur ce dernier pour le nommer de suite…
Par exemple : basTransforms
Les bonnes habitudes font gagner un temps considérable.
Dans ce module que vous ouvrez, vérifiez que la mention Option Explicit est bien mentionnée en entête de module, sinon, écrivez-la.
' **************************************************************************************************
' CreateRectRgn:
' Fonction qui créée une région rectangulaire
'
' CombineRgn:
' Fonction qui combine 2 régions et stocke le résultat dans une troisième :
' Les 2 régions sont combinées selon un mode spécifique.
'
' DeleteObject:
' Fonction qui détruit tout ce qui est objet logique à savoir pen, brush, font, bitmap, region,
' ou encore palette tout en libérant les ressources consommées par le système pour cet objet.
' Bien entendu, une fois détruit, le handle de cet objet n'existera plus à partir de cet instant.
'
' SetWindowRgn:
' Fonction qui définie la région d'une fenêtre pour une fenêtre donnée.
' La région de la fenêtre détermine la zone à l'intérieure de laquelle la fenêtre alloue au système
' la possibilité de dessiner un objet.
' Toutefois, le système n'affiche aucune portion de la fenêtre qui se trouve à l'extérieure de la
' région en question.
' **************************************************************************************************
Option
Explicit
Private
Declare
Function
CreateRectRgn Lib
"gdi32"
(
ByVal
X1 As
Long
, ByVal
Y1 As
Long
, ByVal
_
X2 As
Long
, ByVal
Y2 As
Long
) As
Long
Private
Declare
Function
CombineRgn Lib
"gdi32"
(
ByVal
hDestRgn As
Long
, ByVal
_
hSrcRgn1 As
Long
, ByVal
hSrcRgn2 As
Long
, ByVal
nCombineMode As
Long
) As
Long
Private
Declare
Function
DeleteObject Lib
"gdi32"
(
ByVal
hObject As
Long
) As
Long
Private
Declare
Function
SetWindowRgn Lib
"user32"
(
ByVal
hwnd As
Long
, ByVal
hRgn As
Long
, _
ByVal
bRedraw As
Boolean
) As
Long
Private
Const
RGN_OR =
2
La partie des API étant écrite, il me reste à vous montrer la fonction miracle qui va transformer votre formulaire en étoile…
Vous écrirez ou recopierez le code suivant juste en dessous des déclarations :
Public
Sub
GenerateTransForm
(
ByVal
Frm As
Form, ByVal
Pct As
PictureBox, ByVal
ColorValue As
Long
)
Dim
lngX As
Long
, lngY As
Long
Dim
lngStartX As
Long
, lngStartY As
Long
Dim
lngEndX As
Long
, lngEndY As
Long
Dim
lngHRectregion As
Long
, lngTempHRectRegion As
Long
Dim
lngVoidReturn As
Long
Dim
blnStatus As
Boolean
Frm.Width
=
Frm.ScaleX
(
Pct.Width
, vbPixels, vbTwips)
Frm.Height
=
Frm.ScaleY
(
Pct.Height
, vbPixels, vbTwips)
DoEvents
blnStatus =
False
For
lngX =
0
To
Pct.ScaleWidth
blnStatus =
False
For
lngY =
0
To
Pct.ScaleHeight
If
blnStatus Then
If
Pct.Point
(
lngX, lngY) =
ColorValue Then
lngEndX =
lngX
lngEndY =
lngY
If
lngHRectregion =
0
Then
lngHRectregion =
CreateRectRgn
(
lngStartX, lngStartY, lngEndX +
1
, lngEndY)
Else
lngTempHRectRegion =
CreateRectRgn
(
lngStartX, lngStartY, lngEndX +
1
, lngEndY)
lngVoidReturn =
CombineRgn
(
lngHRectregion, lngHRectregion, lngTempHRectRegion, RGN_OR)
DeleteObject lngTempHRectRegion
End
If
blnStatus =
False
End
If
Else
If
Pct.Point
(
lngX, lngY) <>
ColorValue Then
lngStartX =
lngX
lngStartY =
lngY
lngEndX =
lngX
lngEndY =
lngY
blnStatus =
True
End
If
End
If
Next
' *** Complément de code à ajouter entre les deux blocs Next pour éviter qu'une zone
' *** soit ignorée si le dernier pixel en bas de l'image n'est pas dans la couleur ciblée (ici bleue).
' *** Merci à "David Ughetto" pour cette suggestion...
If
blnStatus Then
lngEndX =
lngX
lngEndY =
lngY
If
lngHRectregion =
0
Then
lngHRectregion =
CreateRectRgn
(
lngStartX, lngStartY, lngEndX +
1
, lngEndY)
Else
lngTempHRectRegion =
CreateRectRgn
(
lngStartX, lngStartY, lngEndX +
1
, lngEndY)
lngVoidReturn =
CombineRgn
(
lngHRectregion, lngHRectregion, lngTempHRectRegion, RGN_OR)
DeleteObject lngTempHRectRegion
End
If
End
If
Next
lngVoidReturn =
SetWindowRgn
(
Frm.hwnd
, lngHRectregion, True
)
lngVoidReturn =
DeleteObject
(
lngHRectregion)
End
Sub
Cette procédure utilise trois arguments (Paramètres) qui sont respectivement l'objet Form, l'objet PictureBox et la combinaison convertie en Long de la palette RGB qui entoure l'objet dessiné, en l'occurrence l'étoile et donc ici, le bleu uni que j'ai appliqué à l'image.
IV-A-1. Comment fonctionne cette procédure ?▲
Dans un premier temps, la procédure définit la largeur et la hauteur du formulaire aux dimensions de l'image, c'est pourquoi je vous ai précisé ci-avant cet avertissement.
Une fois cela fait, elle affecte à un Boolean la valeur False de manière à évaluer quelles actions entreprendre dans le bloc If/End If et ainsi, définir les différentes régions de couleur à détruire.
Si une zone de couleur correspond à l'argument ColorValue, la fonction DeleteObject se charge de détruire la région définie.
Cette procédure s'exécute dans une boucle qui est définie elle-même sur chaque Pixel X de la largeur de l'image et à l'intérieure de laquelle une autre boucle s'occupe de chaque Pixel Y.
Vous enregistrez alors le projet en donnant un nom de fichier à votre Formulaire (frmTransform), au module (basTransform) et enfin au projet (pjtTransform).
Vous pouvez alors fermer la fenêtre du module ; la fenêtre du formulaire réapparaît alors.
IV-B. Définition du code pour le formulaire▲
Il reste pour terminer à écrire quelques lignes pour le formulaire de manière à ce que la fonction soit appelée dès son chargement…
Option
Explicit
Private
Sub
Form_Load
(
)
GenerateTransForm Me, pctStar, RGB
(
0
, 0
, 255
)
' SetFormToTop Me, True
End
Sub
On appelle donc la procédure GenerateTransForm suivie de ses trois paramètres qui sont, comme stipulé plus haut, les objets qui constituent le formulaire.
Pour définir l'argument ColorValue, on associera la fonction RGB() qui prendra ses trois valeurs relatives à la couleur de remplissage de l'arrière-plan à savoir, 0 pour le rouge, 0 pour le vert et 255 pour le bleu.
Point très important
Il est impératif que vous sachiez déterminer en R-V-B la couleur de l'arrière-plan sans quoi, la procédure sera incapable d'agir correctement.
Si vous ignorez la valeur, alors il vous faut ouvrir le fichier contenant l'image dans un logiciel pourvu d'une pipette pour en prendre la couleur et de là, connaître la valeur réelle RVB du niveau de la couleur. (Par acquit de conscience, mettez 0, 0, 254 à l'argument ColorValue et vous constaterez que ça ne marche pas.)
Il y a une ligne commentée dans le code… J'ai fait exprès de la laisser telle que pour vous montrer qu'il est possible de mettre au premier plan la fenêtre de telle sorte à ce qu'elle ne passe jamais en dessous des autres dans l'environnement Windows.
Cette procédure utilise l'API SetWindowPos() avec les arguments qui s'imposent.
Je vous laisse le soin de chercher dans les codes source du Forum l'usage de cette fonction.
V. Test de fonctionnement▲
Après avoir vérifié que tout est bien rédigé, que les propriétés ont bien été définies, vous pouvez appuyer sur la touche F5…
Le formulaire en étoile apparaît alors…
V-A. Correction de la zone de bleus▲
Vous pouvez constater que le pourtour du formulaire est coiffé d'une légère ligne bleue qui est mal nettoyée… Cela n'est pas forcément gênant si la figure est bien dessinée.
En fait, il est délicat d'obtenir quelque chose de parfait, le mélange noir/bleu étant ignoré de la combinaison 0, 0, 255.
Il vous faut effectuer des tests à maintes reprises en ayant plusieurs jeux d'images. Mais je conseille tout de même de conserver une bordure autour de la forme et surtout de faire en sorte que votre forme ne possède aucune couleur identique à celle de l'arrière-plan.
C'est par tests successifs que vous obtiendrez ce que vous souhaitez, j'en parle en connaissance de cause.
VI. Déplacer le formulaire▲
Eh oui, vous vous en doutez, du fait que la propriété BorderStyle est à+ zéro, il n'y a plus de barre de titre, donc impossibilité de déplacer le formulaire…
Il existe plusieurs solutions pour déplacer un formulaire dont la barre de titre a été ôtée… Le principe consiste en fait à déterminer le Rectangle qui constitue la fenêtre du formulaire (je précise Rectangle, car même s'il est transparent et déformé, le formulaire est toujours situé dans une région rectangulaire) et par la même, déterminer les coordonnées XY de départ du pointeur de la souris. Une fois cela isolé, un calcul s'effectue pour déterminer les nouvelles coordonnées XY lorsque la souris a été déplacée et enfin, redessiner le rectangle dans son intégralité.
Dans l'absolu, l'opération est totalement transparente pour l'utilisateur, car les effets graphiques conséquents sont invisibles.
VI-A. Mise en œuvre de la routine de déplacement▲
Pour déplacer le formulaire dans cet état, je vais utiliser deux API supplémentaires associées à un jeu de constantes que je vais exploiter sur l'événement qui gère les déplacements de la souris.
L'événement en question se nomme MouseMove() et il est disponible pour quasiment tous les contrôles posés sur un formulaire. Celui qui nous intéresse, en l'occurrence, est celui du formulaire lui-même.
Vous allez ajouter cette portion de code dans le module basTransforms:
' ReleaseCapture
' Fonction permettant de libérer la capture de la région d'une fenêtre dans le processus en cours
' puis reconstitue le processus d'entrée par la souris a son état normal.
'
' SendMessage
' Fonction qui envoie un message spécifique à une ou plusieurs fenêtres. Aucun résultat n'est retourné
' tant que la procédure d'appel n'a pas intercepté et traité le message contrairement d'ailleurs à la
' fonction PostMessage qui envoie un message dans une file d'attente de processus.
Public
Declare
Function
ReleaseCapture Lib
"user32"
(
) As
Long
Public
Declare
Function
SendMessage Lib
"user32"
Alias "SendMessageA"
(
ByVal
hwnd As
Long
, ByVal
wMsg As
Long
, _
ByVal
wParam As
Long
, lParam As
Any) As
Long
Public
Const
WM_NCLBUTTONDOWN =
&
HA1
Public
Const
HTCAPTION =
2
Comme vous pouvez le constater, les fonctions autant que les constantes sont déclarées en Public.
Il suffit alors de solliciter l'événement MouseMove() précité dans le module du formulaire comme suit :
Private
Sub
Form_MouseMove
(
Button As
Integer
, Shift As
Integer
, X As
Single
, Y As
Single
)
If
Button =
vbLeftButton Then
ReleaseCapture
SendMessage Me.hwnd
, WM_NCLBUTTONDOWN, HTCAPTION, 0
&
End
If
End
Sub
J'ai expressément ajouté un bloc If /End If qui vérifie quel bouton est appuyé et ici, c'est le gauche. cela se traduit par :
Si c'est bien le bouton gauche qui est enfoncé, alors appel respectif des fonctions ReleaseCapture() et SendMessage().
Vous pouvez alors tester de nouveau en appuyant sur F5 et vous constatez que vous pouvez déplacer le formulaire à votre guise.
VII. Conclusion▲
Ce tutoriel peut s'avérer utile pour les développeurs exigeants et surtout originaux qui souhaitent créer des formulaires qui sortent de l'ordinaire…
Bien entendu, je préconise de réserver ce genre de conception à des applications plutôt ludiques ou multimédias et en aucun cas à des projets de gestion… Vous pouvez constater toute la puissance de Visual Basic qui initialement ne prévoit pas de développer des projets comme celui-ci.
Je vous laisse vous entraîner avec cette pratique. Si vous rencontrez des difficultés ou si j'ai omis de préciser quelque chose qui reste obscur, n'hésitez pas à m'en faire part.
Pour compléter ce tutoriel, vous pouvez télécharger le projet de démonstration ici…