Site Perso de

Thomas JANNAUD

Des actualités personnelles sous un style impersonnel, et inversement.



Tutoriel VB6/VBA
Bases pour créer une belle app en VB6/VBA + astuces et codes sources 17 Mars 2008
visual basic

Pour coder en Visual Basic, il faut disposer de Visual Studio par exemple, ou de Visual Basic. (les "Express Edition" sont mises gracieusement à disposition par Microsoft). J'utilise la version 6 (qui correspond à l'année 1998) car celles d'après (les ".net") n'ont pas survécu, et c'est cette version qu'on retrouve dans VBA (les macros Excel, ...)

Rappelons brièvement les avantages de Visual Basic :

Vous trouverez dans cet ouvrage de quoi apprendre VB6. C'est bien simple, ce livre m'a tout appris. Vous aurez dans ce livre à construire des petites applications simples, pas à pas, et vous allez découvrir 1001 techniques essentielles, entre le multimédia, les bases de données, Excel, faire de vraies applications professionnelles, ... Mon premier livre de programmation et sans doute le meilleur investissement que j'ai jamais fait.
D'autre part, par expérience, vous verrez qu'un site internet ne remplacera jamais un livre papier.
Cette version est en français.

Conseils

Je ne vous conseillerai que trop le site VBFrance et sa mailing list : il y a beaucoup d'astuces, ... que l'on ne peut pas inventer tout seul. D'ailleurs je ne sais même pas comment les gens les trouvent. Il y a plein de petits bouts de code dont on aura toujours besoin à un moment ou à un autre dans notre vie, comme mettre des icones à nos menus, rendre une partie de la fenêtre transparente, ...

Je vous conseille aussi d'aller voir ma page de codes sources, où je mets pas mal de programmes en ligne, ce qui peut être très utile pour avoir une vision d'ensemble de la physionomie d'un programme en VB

Créer une interface utilisateur

Pour comprendre la logique de Visual Basic, il faut se dire qu'un logiciel, c'est 2 choses : une interface (utilisateur), et le code qui est derrière pour gérer les événements (clics, ...) et intéragir avec l'utilisateur par le biais justement de l'interface. Et bien VB est fait exactement de cette manière : il y a une partie "interface", c'est elle qui s'ouvre en premier quand on fait nouveau projet, et une autre partie, le code qui se cache derrière.
Vous pouvez prendre des controles dans la barre sur la gauche, et les glisser - déposer sur la fenêtre. Si vous trouvez que vous n'avez pas assez de contrôles ou si vous en cherchez un en particulier, faites Ctrl+T ou Projet->Components et faites ce qu'il vous plaît. Construisez votre interface de cette manière. Dans l'espèce de petit tableur sur la droite figurent les propriétés des contrôles (ou plutôt du contrôle sélectionné). Vous pouvez changer le nom, la propriété Caption, ... si ça vous chante. Double cliquez sur un contrôle pour arriver à la partie "Code". Visual Basic crée automatiquement pour vous une nouvelle procédure / fonction / méthode associée à l'événement 'Click' sur ce contrôle. Sur la liste déroulante tout en haut à gauche figurent tous les contrôles de la fenêtre, et sur la liste de droite l'événement associé (si vous décidez d'associer un événement).
Et voila, vous êtes prêts à coder !

Astuce pour avoir de l'aide

Certaines fonctions internes de Visual Basic ne sont pas complètement détaillées ici, et ce pour 2 raisons :

Rappel : [n] désigne que n est un paramètre optionnel

Structures de controle

Il n'y a pas d'accolades en Visual Basic pour déterminer des blocs de code, comme en C++ pour dire que faire dans une boucle for. Ici, cela ressemblerait plus à du html par exemple : on ouvre une "balise", et on la referme plus loin.

Dim x as integer, y as double, z as string
(devinez !). Autres types possibles pour la déclaration de variables : float, byte, currency (c'est quand meme un nombre) et d'autres plus loin. Float, currency et double sont pour les nombres à virgule, string les chaines de caractères, byte les nombres de 0 à 255 (qui tiennent donc sur 1 octet = "byte" en anglais)
x = 2
et là ... ? :D
Option Explicit
Se situe tout en haut d'une feuille ("Form"). Toutes les variables déclarées ensuite (avant la prochaine fonction / événement) seront globales à la feuille. Pour créer des variables TRES globales, créez un module (Projet -> Ajouter -> Module), et dans le Option Explicit de celui-ci mettez les variables que vous voulez.
Conditionnement
if ... then
...
elseif ... then 'ceci est un commentaire : elseif est ATTACHE !
...
elseif
...
else
...
end if

On peut aussi écrire une condition if sur une seule ligne, sans utiliser de end if if (x < 0) then x = (-x)

le Select case : remplace le if i = 1 then ... else if i = 2 then ... else ... par exemple. Ça fait plus joli mais ça sert honnêtement pas à grand chose.
Select case nom
case "papa"
...
case "maman"
...
case else
...
End Select

Remarque : avec les nombres, on peut aussi avoir : case >= 3 par exemple.

Goto

La fameuse instruction goto, très décriée des "pros". Moi je la trouve bien pratique.

goto label
...
label:

Le label peut se situer avant l'instruction goto, dans le code ! Mais il doit toujours être dans la même procédure/fonction !! Goto peut servir à tout, même à sauter plusieurs boucles à la fois ! Plus qu'utile.

Boucles
for k = 1 to 10 [step 2]
...
next k


do [while condition]
...
loop

On peut mettre 'loop while' si l'on veut (mais rien mettre après do alors). On peut aussi remplacer while par until (pour les conditions négatives)

L'instruction 'break' pour les boucles for ou do...loop est Exit For et Exit Do et amène directement à la sortie de la boucle.

for each a in b 'b doit être une 'collection' (défini plus loin)
...
next
Déclaration de fonctions
private function mafonction(a as integer, b as string, optional c = 2) as double
'ou un autre type ! 
...
mafonction = 3.1415
end function

A noter que l'on peut placer ses fonctions n'importe où dans le code, en particulier que l'on n'est pas obligé de placer plus haut sur le fichier une fonction qui est utilisée par une autre plus bas. Il n'y a pas non plus de prototype à écrire.

public, private, ...
Déclaration d'une fonction, public si la fonction est dans un module et doit etre visible d'ailleurs
Procédures
private sub mafonction(a as integer, b as string, optional c = 2)
...
...
end sub

C'est la déclaration d'une fonction qui ne renvoie pas d'argument ; autrement dit, une procédure.

Paramètres passés par référence ou par valeur :
écrire mafonction(ByRef a as integer, byval b as integer, byref c as integer) ... pour avoir a et c passés par référence au lieu de par valeur, qui est par défaut. Je conseille FORTEMENT de toujours mettre byval, surtout quand vous passez des paramètres comme des chaînes ou des classes, même si c'est par défaut. On ne sait jamais ! Et le jour où vous vous en rendrez compte, comme moi vous comprendrez que vous venez de perdre 4 heures de suite de débogage. A bon entendeur... ;)
a = f(x, y, z)
pour appeler f et stocker le résultat dans a
Mais aussi... f x,y,z
Quand f est une fonction et qu'on se ficher de son résultat (elle exécute quelque chose quand même et renvoie si oui ou non elle a fait une erreur par exemple) ou quand f est une procédure.
Call f(x, y, z)
La façon non paresseuse d'appeler une procédure, plus appréciée du compilateur
Static a as integer
Si vous mettez ce code dans une fonction, cela déclare la variable a comme une variable statique : sa valeur sera conservée après la fin de la fonction (ou de la procédure), ce qui peut être pratique. En gros, c'est comme une variable globale, mais dont la visibilité est limitée à la procédure ou fonction.

Chaines de caractères

Prélude : c'est un type un peu particulier : à la fois tableau et variable, on ne sait jamais. En VB, contrairement à beaucoup d'autres langages objets, la chaîne est comme une variable integer par exemple : on peut la passer en paramètre sans craindre que ce soit une référence seulement qui soit passée, ... Bref, il ne faut pas s'inquiéter ici.
D'autre part, attention !!! Les indices commencent donc à 1, et non pas à 0 comme c'est souvent le cas dans d'autres langages !!!

Dim a as string
dim b as string * 10 'limite la taille de b à 10 caractères
a = "bonjour"
b = "thomasthomas" 'b vaudra "thomasthom" en réalité
len(a)
diminutif de "length" : nombre de caractères de la chaine
LTrim(a), RTrim()
suppriment les espaces en tête (L("eft")Trim) ou en queue (R("ight")Trim) de la chaine
chaine1 & chaine2
concaténation de 2 chaines
Mid("bonjour", 3, 4)
"njou" : 3 dit où commencer, et 4 le nombre de caractères à prendre. Ne pas mettre d'argument à la place de 4 permet de prendre la chaîne jusqu'à la fin.
Mid n'est pas une fonction seulement, c'est aussi une "caractéristique" : on peut faire mid(a, 3, 4) = "abcd", et ainsi a -> "boabcdr"
L'expression split
dim tableau() as string 'déclare un tableau sans en préciser la taille
tableau = split("bonjour les amis", " ")

ultra-puissant : si on le met à " " ça va détacher les mots d'une phrase. Si on le met à "o" ça va détacher "bonjour" en {"b","nj","r"}. On peut ensuite accéder à ces parties avec tableau(1), tableau(2,), ... : tableau.lbound et tableau.ubound donnent les indices de début et de fin du tableau créé de cette manière. (l pour 'lower' et u pour 'upper')

s1 = Replace(s1, "toto", "tagada")
Remplace toutes les occurences d'un certain motif dans une chaine. (ici : "toto" par "tagada").
Instr(s1, s2)
Permet de savoir si une chaine est incluse dans une autre ; renvoie alors la première position de celle-ci, 0 sinon.

Tableaux

La création de tableaux est quelque chose de vraiment très sympa, en VB : les indices commencent où l'on veut !
D'autre part, les tableaux sont soit de taille fixée au départ, soit modelables

Dim t(1 to 10) as integer
Déclare un tableau de 10 entiers, indexé par des indices allant de 1 à 10
Dim u(2 to 4, 5 to 8) as string
Déclare un tableau de 3 lignes (indexées par des indices allant de 2 à 4) et de 4 colonnes (5 -> 8) contenant des chaines de caractères. Point important : on a déjà dit que les chaines de caractères ne sont pas vraiment traitées comme des pointeurs en VB, et c'est donc tant mieux, sinon rien ne nous dit que la déclaration d'un tableau comme ça ne crée pas en fait 3 * 4 chaines identiques (i.e si on change une chaine, toutes les autres changent puisque ce seraient en fait des pointeurs). Mais tout va bien, ce n'est pas ça qui se passe ! Attention cependant aux tableaux sur des objets à nous, parce que la oui, c'est des pointeurs. Il faut alors initialiser chaque case du tableau avec t(i, j) = new ...
Remarque sur les indices qui commencent où l'on veut
Comme vous pouvez le constater sur l'exemple ci-dessus, ce n'est pas vraiment agréable. Faites les commencez à 0 ou à 1 je dirai, suivant votre habitude, mais quand même plutôt à 1 vu la syntaxe des boucles for de VB. (en C++ ça va plus vite de mettre for(i = 0; i < n; i++) que i<= n)
Nombre de dimensions
Il me semble qu'on a le droit à des tableaux de dimension jusqu'à 64, mais ce n'est pas certain. En pratique, 2 dimensions suffisent (même si l'on gère des ensembles à 3 dimensions ! dim t(1 to n, 1 to 3) as Double et on a alors 3 colonnes pour gérer les 3 coordonnées de nos n points)
Tableaux de taille non fixée au départ
Dim t() as integer
... (et plus loin dans le code)
Redim [preserve] t(1 to n)

Preserve sert à faire en sorte que si on aggrandit ou diminue notre tableau de 1 case seulement (par exemple), VB va faire en sorte de conserver le maximum d'éléments à leur valeur d'origine ; en clair il va recopier une partie de notre ancien tableau dans le nouveau

A noter que l'on peut déclarer des tableaux à plusieurs dimensions sans connaitre qu'une seule des dimensions (la dernière), sinon c'est impossible.

Les listes ou 'Collection'

Ce sont des objets assez simples et bien faits, mais pas forcément très rapides (ne les utilisez pas pour des listes de plus de 10000 éléments par exemple, surtout si vous voulez avir un accès séquentiel). Il n'y a pas beaucoup de fonctions toutes faites sur les Collection (comme inverser la collection, la trier, ...).

Dim maliste as New Collection
Ceci crée une nouvelle liste. Elles sont toujours non typées !. Les indices commencent toujours à 1.
maliste.AddItem "bonjour"
Remarquez qu'il y a des arguments optionnels pour insérer un élément à un indice particulier
maliste.item(n)
maliste.count
le nombre d'éléments de la liste.

Faites attention lorsque vous parcourez une liste, si vous supprimez des éléments, l'indice des éléments suivants peut changer !
Et plutot que de faire for i = 1 to maliste.count ...maliste.item(i)... next i, faites plutot for each element in maliste, ... element... next sinon l'ordinateur va à chaque fois repartir de l'indice 1 pour calculer item(i) ce qui est très lent, surtout vers la fin de la liste, au lieu de tout parcourir d'un seul bloc.

Les types prédéfinis, ou enregistrements

Personnellement je ne les trouve pas forcément utiles, mais bon, faites comme bon vous semble. La seule utilité que j'y vois, c'est que si l'on a la liste des positions x d'objets (de dessins par exemple) et une autre la liste des y, et dans une autre la liste des z, on peut tout combiner en un seul ce qui peut faire plus propre au niveau du code.

Mais VB ne gère pas très bien ce genre de type, donc vaut mieux ne pas trop faire de choses comme ca. Pour ne pas prendre de risque, personnellement, j'utilise un tableau à plusieurs dimensions, même si c'est plus moche au niveau du code.

Attention : je parle ici des versions jusqu'à la 6, car il parait que ça a changé depuis et que VB est devenu nettement plus orienté objet. En tout cas, jusque dans là 6, on ne peut pas se faire de tableaux de type prédéfini.

Contrairement à ce qu'on pourrait croire, quand on passe un type prédéfini dans une fonction, il est passé par valeur aussi (à moins de préciser ByRef). En gros il y a une copie de faite.

Type montype (à déclarer dans un module, c'est préférable)
x as integer
y as integer
nom as string
aaa as unautretypeamoi
end type

et pour initialiser :

dim a as montype
a.x = 2
a.y = 4
a.nom = "bonjour"
...

Les boites de dialogue

Ne pas abuser de leur codage simplissime ! Ça devient très vite énervant pour l'utilisateur.

a = Msgbox("bonjour ça va ?", vbyesno + vbquestion + vbdefaultbutton1, "un titre)
Renvoie vbyes si l'utilisateur a cliqué sur "Oui", vbno sinon. Si on se fiche de la valeur renvoyée, on peut mettre comme d'habitude (cf fin du paragraphe sur les fonctions) msgbox "ça va ?", vb..... On n'est pas obligé de choisir l'image (vbcritical, vbinformation, vbquestion ou vbexclamation), et on peut mettre vbokonly, vbyesnocancel, ... en boutons (VB nous propose tout seul des possibilités)
Autre type de boite : a = inputbox("Entrez votre age ici", "textepardefaut", "titre ici")
Tandis qu'on se fiche souvent du résultat de la première (le logiciel annonce une erreur, une sauvegarde qui s'est bien passée, ...) dans la deuxième il est plus important. Il est en type string toujours, donc vérifier que l'utilisateur a bien rentré un nombre, ... Ça renvoie "" si l'utilisateur clique sur "Annuler"
Les boites plus "intelligentes" : choisir une couleur, sauvegarder un fichier, ...
Windows met gracieusement à notre disposition son savoir faire en matière de boîtes de dialogue : nous sommes tous habitués à ce que quand on fait "Ouvrir" ou "Enregistrer Sous" une certaine boite un peu comme l'explorateur apparaisse. L'utilisateur est familié avec ce type de boites donc cette fois il ne faut pas s'en priver ! Ctrl+T pour faire apparaître la grande liste des contrôles OCX à rajouter, et choisir Microsoft Common Dialog. Elle apparaît alors sur la palette de gauche. Il faut la placer sur la fenêtre, n'importe où de toute façon elle sera invisible. Nommons-la "boite" pour simplifier.

Voici divers codes :
Penser au préalable à fixer la propriété CancelError de la boîte à True : cela nous renseigne sur le fait que l'utilisateur a cliqué sur "Annuler"

private sub changer_couleur()
on error goto erreur:
boite.showColor
Me.backColor = boite.color
exit sub
erreur:
Msgbox "Vous n'avez pas souhaité changer la couleur de fond"
	'boite de dialogue qui énerve l'utilisateur
end sub

private sub ouvrir()
on error goto erreur:
boite.showOpen
if boite.filename <> "" then
ouvrir_fichier(boite.folder & boite.filename)
' c'est à vous d'écrire le code d'ouverture d'un fichier !!! Vous croyez quoi :)
' (cf système de fichiers pour plus d'infos)
exit sub
end if
erreur:
Msgbox "Vous n'avez pas souhaité ouvrir de fichier" 'boite de dialogue tout aussi intempestive
end sub

Et il y a encore beaucoup d'autres boites : showPrint, showSave, ...

Les classes

Pour en créer : Fichier, ajouter -> Class Module (la même boite que pour les Form, Module, ...). Pensez à renseigner le champ "name" de votre classe. Ce sera un peu comme un nouveau type pour vos variables. A une différence : c'est que cette fois, c'est un pointeur sur votre classe, c'est toujours passé par référence, donc il faut faire attention, mais c'est souvent plus pratique.

Option Explicit
public nom as string
public age as integer
private poids as integer

private function mafonctionpriveequifaitjenesaisquoi(ByVal a as integer)
...
End Function

// Renseigner les champs d'initialisation, c'est mieux (équivalent à un constructeur)

Puis dans le code (associé à une fenetre ou à un module ou à...) :
dim thomas as new nomdemaclasse
thomas.age = 22
thomas.nom = "thomas"
dim pierre as nomdemaclasse

set pierre = thomas ' il faut souvent associer set à l'assignation à une valeur en ce qui concerne les objets (ex : les images, ...)
msgbox pierre.age ' affiche 22 : pierre est un POINTEUR ou une REFERENCE à THOMAS. Autrement dit 2 noms pour le même objet. Changer l'un change l'autre.

Lecture / écriture dans les fichiers

Une partie que les gens trouvent souvent dure. C'est dommage, je la trouve vraiment très simple en Visual Basic. Il y a deux types d'accès à un fichier, que ce soit en lecture, ou en écriture. Soit on lit un fichier ligne par ligne jusqu'à (si l'on veut) la dernière, soit on demande de lire le n ième octet d'un fichier. (idem pour l'écriture). Quelque chose qui fait peur et qui peut être contre-intuitif est qu'il faut toujours pour la sauvegarde écraser l'ancien fichier et le remplacer entièrement par un nouveau. (C'est comme ca. Pensez à l'implémentation sur le disque dur d'un fichier, et à ce qu'impliquerait l'insertion d'un seul caractère en plein milieu d'un fichier par exemple).

Exemples

Pour ouvrir un fichier (qu'importe le type) : Windows accède à un fichier par un flux de données, et il ouvre donc un canal. En gros, au lieu de dire "lis la ligne du fichier "Bureau\\toto.txt"" on va dire "lis la ligne du fichier associé au flux du canal 34".

La question qui vient naturellement (ou pas) sur les lèvres est : comment savoir quel est le numéro de canal ?
Réponse : C'est nous qui le choisissons. Mais comme on n'a pas le droit à un numéro déjà pris, il y a une fonction toute faite qui gère ça : FreeFile() -> renvoie un entier.

Passons à la pratique !

Le mode séquentiel
C'est à dire tous les trucs les uns à la suite des autres
dim f as integer
dim ligne as string
f = freefile()
open "C:\\.....\\fichier.doc" for append as #f
do until EOF(f)
line input #f, ligne
'exécuter le code en rapport avec la ligne lue ici 
loop
close #f 'on ferme le flux / canal

'EOF' signifie "End of File". Donc on lit les lignes jusqu'à ce que la fin du fichier soit atteinte. Si l'on sait à l'avance le nombre de lignes, par exemple 10, ou bien s'il est écrit dans la toute première ligne de notre fichier :

dim f as integer, n as integer, i as integer
dim ligne as string
f = freefile()
open "C:\\.....\\fichier.doc" for input as #f
line input #f, ligne
n = cint(ligne)
for i = 1 to n
line input #f, ligne
'exécuter le code en rapport avec la ligne lue ici
Next i
Close #f 'A NE PAS OUBLIER !!!!

Pour écrire dans le fichier, c'est pareil :

'si le fichier existe déjà on le supprime
if (dir("C:\\....\\toto.txt") <> "" ) then
Kill "C:\\....\\toto.txt"
end if

dim f as integer, i as integer
f = freefile()
open "C:\\....\\toto.txt" for append as #f
print #f, n
for i = 1 to n
print #f, nom_contact(i) 'si on a un tableau de contacts
next i
close #f 'A NE PAS OUBLIER !!!!

Bref, c'est facile, vous voyez ! printf #numerofichier, cequonveut et line input #numerofichier, cequonveut.
Remarque :Vous pouvez utiliser "input" au lieu de "line input", mais cela lira les mots un par un, au lieu de lire ligne par ligne. C'est pas forcément plus pratique, car si on écrit le nom de l'utilisateur, et qu'on veut le lire au prochain lancement du programme, et que le nom de famille c'est De Gaulle, et bien on va lire un mot au lieu de deux, et tout le reste va être décalé.

Le mode binaire
C'est encore plus simple :
dim f as integer
dim b() as byte
f = freefile()
redim b(1 to filelen("C:\\...\\toto.txt")
open "C:\\...\\toto.txt" for binary as #f
get #f, 1, b
close #f

Et en écriture : idem avec put au lieu de get. (pensez à effacer le fichier au préalable pour éviter certains bugs)

Ici, b() est un tableau d'octets ('byte' en anglais). A la lecture, on lui donne la taille du fichier puis on l'initialise avec get.
A l'écriture, on possède un tableau d'octets (déjà affectés : ce sont les données à enregistrer) et on envoie ça dans le fichier. A noter que le 1 de "get #f, 1, b" signifie ici : lire les octets du fichier du canal numéro f à partir du 1er et placer ça dans b(). Dans notre cas celà revient à lire tout le fichier puisque b() a la même taille que le fichier (à cause de filelen()). Cependant on peut très bien décider de lire les octets un par un, et on pourrait faire dim c as byte ...for i = 1 to n... get #f, i , c

A noter que si l'on veut stocker une variable plus grosse, int par exemple (4 octets peut-être, ou plus ??) on n'a pas besoin de faire de multiples divisions euclidiennes pour connaitre l'écriture en base hexadécimale de notre entier, et stocker ça byte par byte (octet par octet). L'ordinateur gère ça tout seul. Il faut quand même faire gaffe au décalage induit par le fait que ça prend 4 octets.

Exemple :
dim a as integer, b as integer

'code d'ouverture avec freefile, ...
get #f, 1, a
get #f, 5, b
close #f
Le mode aléatoire
Il existe en fait un troisième mode d'accès à un fichier, mais je le trouve tellement pas pratique que je ne l'ai jamais utilisé. C'est le mode "aléatoire" : open "C:\\..." for Random as #f. C'est un peu comme le mode Binary, sauf que là il faut dire à l'avance quels types de données seront où. En fait c'est surtout pour stocker des enregistrements (= types prédéfinis, du genre "nom" puis "n° téléphone") où il faut définir par avance la taille que l'on réserve à chacun. Par exemple 20 caractères max. pour le nom. Je trouve ce mode un peu bête parce que l'on ne sait jamais par avance ce que va rentrer l'utilisateur, et d'autre part, si quelqu'un a un nom à 5 lettres seulement cela fait 15 caractères non utilisés (même si vu la taille des disques durs de nos jours, on peut se le permettre).

Le système de fichiers

Le code qui suit est un peu complexe, et la documentation à son sujet est peu fournie sur internet. La raison est qu'il utilise un truc un peu "louche" : la fonction CreateObject ; en gros ça permet d'utiliser des fonctions propres à Windows mais qui ne sont pas des fonctions connues de VB.

Dim oFs As Object
Set oFs = CreateObject("Scripting.FileSystemObject")

Dim oDossier As Object
Dim oSousDossier As Object
Dim oSousDossiers As Object
Dim sDossier As String
Dim f, f2

Set oDossier = oFs.GetFolder("C:\\...\\Bureau\\")
'oDossier est l'objet "Dossier" associé à la chaîne de caractères (ici : le bureau)
Set oSousDossiers = oDossier.SubFolders
'tous les sous-dossiers du Bureau. C'est une collection d'objets 'Dossiers'
Set f = oDossier.Files
'la collection de tous les fichiers de l'objet 'Dossier', donc tous les fichiers du bureau

for Each f2 in f
me.print f2.Name
Next

For Each oSousDossier In oSousDossiers
sDossier = sNomDossier & oSousDossier.Name & "\\"
Me.Print sDossier
Next

Gestion des événements et des objets

La gestion d'événements est intimement liée à l'existence d'une interface graphique. La création de celle-ci est développée en tête de cette page. Pour gérer les événements associés à des objets, double-cliquez sur l'un deux, puis dans la fenêtre du code, dans la liste déroulante en haut à droite, sélectionnez l'événement qui vous intéresse. Vous avez un choix assez grand. Utilisez ensuite les propriétés fournies (ou pas) en argument pour faire ce que vous voulez (ex : mousedown renvoie le x et le y du curseur de la souris en coordonnées relatives, où l'origine est en haut à gauche de l'objet). Les procédures ainsi définies peuvent être appelées tout comme n'importe quelle autre procédure de votre confection.

Pour accéder aux propriétés d'un objet, donnez le nom de l'objet puis faites "." (sans les guillemets ^^). Le plus simple est d'écrire Me. Me ça veut dire "moi" en anglais. En clair, c'est votre fenêtre. '.' va ensuite vous donner la liste de tous les objets sur votre fenêtre. Vous pouvez bien entendu vous adresser au contrôle d'une autre fenêtre (par exemple Form2) en faisant Form2. ... Form2 doit par contre être loadée, ce qui ce fait comme suit :

Load Form2
Form2.show 'si vous voulez
Form2.Label1.Caption = "bonjour ! je suis contrôlé depuis le code de la form1"
'il faut déjà avoir un label1 sur Form2

On ne peut pas vraiment créer des objets comme ça, à la volée, c'est pas fait pour, et VB va très vite ramer : le mieux est de placer vos objets dès le début. Mais on peut quand même le faire si l'envie nous en dit : Créer un contrôle, n'importe lequel, et mettez sa propriété index à 0. (sans le point, c'était un signe de ponctuation cette fois). Disons que c'est Text1(0). Dans le code :

For i = 1 to 10
Load Text1(i)
Text1(i).Visible = true
Text1(i).top = text1(i - 1) + 300
Next i

...

Unload Text1(i) pour en supprimer un.

Propriétés usuelles

Vous apprendrez très vite les propriétés des objets si vous vous amusez à lire les listes déroulantes qui s'offrent à vous, dans le code. Les propriétés principales restent .Visible (pour cacher un contrôle ou pas), .height, .left, .width, .top (hauteur, largeur, position), .caption, .text, .ToolTipText (la petite bulle d'aide qui apparait quand on laisse la souris 5 secondes de suite sur le même contrôle), .AddItem (pour les list, combobox, ...), .Tag (c'est une variable propre à chaque contrôle qui ne sert à rienen tant que tel, mais pourtant très utile et on peut y mettre ce qu'on veut : chaîne de caractères, entier, ...

Exemple d'utilisation : vous avez plein de boites de saisie de texte, et vous voulez que certaines aient une saisie alpha-numérique bloquée. Et bien vous allez mettre toutes vos textBox avec le même nom mais un index différent, mettre par exemple "chiffre" dans tous les Tag de vos TextBox bloquées, et après dans l'événement keypress, vous allez gérer suivant que le .Tag de votre textbox soit à "chiffre" ou pas.

Evenements et contrôles particuliers

La PictureBox

C'est là dedans que je fais tous les dessins que j'ai à faire, c'est fait pour, comme son nom l'indique. Je vous conseille de mettre sa propriété scaleMode à Pixel au lieu de Twip sinon les effets peuvent être inattendus. En gros, 1 pixel = 15-20 twips. On se demande bien à quoi peuvent servir les twips étan donné qu'un pixel est une unité indivisible, mais bon... Mettez aussi AutoRedraw à True, sinon si vous placez une autre fenêtre devant votre PictureBox, ça va effacer le dessin dessus.

Me.Picture1.line(x1, y1)-(x2, y2), vbblue [,B ou BF] : trace une ligne bleue sur la pictureBox Picture1 de la fenêtre ; B veut dire que ça fait pas une ligne, mais le rectangle associé, et BF dit que ce rectangle sera rempli ('box' et 'box full')

Dim a as stdPicture
set a = loadPicture("C:\\...\\image.jpg ou bmp")
Me.Picture1.Paint(a, x, y[, width, height])
'si l'on veut faire un aggrandissement ou pas de notre image

Les coordonnées commencent toujours en haut à gauche d'un contrôle, et l'axe des y est orienté vers le bas, ce qui est assez déroutant, pour le moins au début

SavePicture Me.Picture1.Image, "C:\\Bureau\\image.bmp" pour sauvegarder une image. Cela veut dire que ce qui est dessiné sur la PictureBox est stocké/accessible via la propriété image de celle-ci.

Remarque sur Direct X

Vous voyez qu'il est assez simple d'utiliser une PictureBox, alors qu'il n'en est pas de même pour DirectX. Comment choisir ? Je vous conseille de faire d'abord votre jeu (ou autre) en dessinant sur une PictureBox, et si c'est lent, de passer à DirectX. L'avantage de DirectX, c'est qu'on se fait une Sub spéciale où l'on dessine tout. Alors que sur une PictureBox, il faut effacer ce qu'on déplace, pour le re-dessiner plus loin, ce qui est plus contraignant. En gros pour un serpent une PictureBox ira très bien. Après... à vous de voir ce qui vous convient le mieux. Il y a au moins 5 codes utilisant DirectDraw dans la partie codes sources. Faites des copier-coller pour utiliser DDraw à votre tour !

Commandes en vrac (Excel, gestion des touches, fichiers *.res, ...)

Randomize Timer puis rand
renvoie un nombre tiré au sort entre 0 et 1. Randomize n'est à faire qu'une fois, en début de programme, pour réinitialiser le générateur de nombre aléatoire.
Timer
C'est le nombre de secondes écoulées depuis 00h00. Très précis. Permet de chronométrer des choses au millième près.
CInt(), CDbl(), CString, ...
Ces fonctions convertissent n'importe quoi en Int, Double, String, ... Attention : Cint("2+3") ne marche pas ! Il faut écrire vous même un algorithme pour ce genre de chose, mais on n'en a pas besoin souvent non plus ! (cf codes sources)
Format()
Transforme n'importe quoi en une chaîne de caractères avec les spécifications voulues. Exemple : format(3.14, "00.0000") = "03.1400". On peut remplacer les 0 par des # pour ne pas mettre de 0 inutilement : Format(2.345678, "#.##") = "2.34"
isnumeric()
Renvoie true si une chaîne ne caractère peut être convertie en nombre grâce aux fonctions de conversions CInt, Cdbl ou C...
Asc et Chr
Renvoient respectivement le code Ascii d'un caractère, et le caractère associé à un code Ascii. Très utilisés en pratique (cf un peu plus bas gestion des appuis touches).
Set
Il faut souvent mettre Set devant le nom d'un objet pour lui assigner quelque chose. Exemple : Set liste as new Collection. Ça veut dire que liste était déjà une ... liste/Collection, mais qu'on laisse tomber celle-ci pour en recréer une nouvelle.
DoEvents
Demande à l'ordinateur de faire une "pause" à notre programme pour qu'il puisse traiter tout ce qu'il a en mémoire et qu'il veut faire depuis longtemps. A utiliser avec parcimonie, sauf peut être de temps en temps dans certaines boucles.
Créer un exécutable
Fichier->Make project1.exe (le nom de votre projet à la place de Project1) pour créer un .exe. Attention, si vous utilisez dans votre programme des images de votre dossier image loadées depuis une ligne de commande type Loadpicture("C:\\Bureau\\Thomas\\monimage.jpg"), il y a de très fortes chances pour que ça ne marche pas chez quelqu'un d'autre. Faites vous un dossier Data par exemple, ou Images, que vous placez dans le même dossier que votre projet. Référez vous aux images par App.Folder & "\\Images\\monimage.jpg", ce qui est une adresse relative, et non plus absolue. App est l'abbréviation de "Application". Il faudra ainsi livrer votre programme avec le dossier Images, et dire aux utilisateurs de placer ce dernier dans le même dossier que le .exe.
Créer un Setup
On en a souvent besoin, par exemple si votre programme s'accompagne d'images, et que le dossier images doit être placé dans le même dossier que l'exécutable, ou bien si votre programme utilise des dll peu communes, ce qui arrive souvent, car lorsque VB s'installe, il installe des tas de contrôles (MSFlexGrid, ...) qui sont donc sur votre ordinateur, mais pas sur celui d'un utilisateur lambda. Il y a un utilitaire spécial, livré avec VB, qui permet de faire un Setup juste avec votre fichier .exe. Il s'agit de "Assistant Empaquetage et Déploiement". Il va déterminer tout seul quels sont les composants à inclure à votre projet qui risquent de ne pas être sur un autre ordinateur. Il fonctionne très bien, mais par contre c'est à vous de dire qu'il faut inclure le dossier 'images' par exemple.
Les 'handle'
A chaque nouveau contrôle créé Windows attribue un numéro unique permettant de l'identifier. Pour tous les contrôles, même le contrôle inclut dans le 4eme onglet du Frame du 2eme onglet. Le handle est accessible par moncontrole.hwnd. Connaissant le handle d'un objet, on peut donc espérer demander à Windows certaines choses, par exemple quelle est la taille du contrôle de hwnd numéro... ou qu'est ce qu'il y a écrit dedans. C'est ce que fait cette source. Le truc aussi c'est que ça utilise un API qui donne le hwnd du contrôle sous la souris. Mais c'est pas magique qu'un tel API existe, puisque Windows doit toujours savoir si on clique à qui (= quel contrôle = quel handle, donc) transmettre l'événement.

Excel

Mettre tout d'abord Excel en référence au projet (comme on le ferait pour une dll par exemple) : Projet -> References -> Cocher Excel

dim xlapp as Object, xlbook as object, xlsheet as object
Set xlapp = New Excel.Application
Set xlbook = xlapp.Workbooks.Open("C:\\Bureau\\toto.xls")
Set xlsheet = xlbook.Sheets("Aube")
xlsheet.Cells(ligne, colonne).Value = "bonjour"
xlapp.ActiveWorkbook.Close False
xlapp.Quit
Set xlapp = Nothing

Gestion des appuis touches sur le clavier

Pour interdire la saisie autre que numérique par exemple, dans un TextBox : dans l'événement keyPress, on reçoit KeyAscii. On peut alors faire if (KeyAscii < Asc('0') Or KeyAscii > Asc('9')) then KeyAscii = 0. De même, si on décide (pour rire), qu'à chaque fois qu'un 'a' est tapé, de le remplacer par un 'b', on fait if KeyAscii = Asc('a') then KeyAscii = Asc('b').

On se sert souvent du KeyAscii = 13 (touche Entrée) pour que l'utilisateur n'ait qu'à appuyer sur Entrée, au lieu de relâcher le clavier pour se servir de sa souris et clicquer sur "Ok" ou "Valider" par exemple : If KeyAscii = 13 then Call Command_Valider_Click()

Si vous avez juste besoin de gérer un peu les touches dans un jeu qui ne va pas forcément vite (si si ça existe ! cf le jeu worms dans ma page de codes sources, et il marche très bien) vous pouvez utiliser l'événement keydown ou keypress de la form (je recommande plutôt keydown, car keypress c'est plus pour les lettres que pour les touches du genre flèches, control, ...).
Sinon ...

Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
'à déclarer dans Option Explicit, ou en public dans un module

dim a as integer
a = GetAsyncKeyState(37) 'flèche gauche (38, 39, 40, correspondent
			' aux flèches droite, bas, haut, dans un ordre à déterminer)
if a <> 0 then ...
end if

Attention : a = 0 si et seulement si l'état de la touche n'a pas changé depuis le dernier appel à la fonction (avec la même touche en paramètre) c'est à dire que si la dernière fois c'était appuyé, le joueur a laissé enfoncé la touche depuis. C'est pour ça qu'il faut initialiser à Form_load() en espérant que le joueur n'appuie sur aucune touche au moment où le jeu se lance, et faire du getasynckeystate sur tous les numéros qu'on utilisera.

Fichiers *.res

Téléchargez ici une source utilisant les fichiers .res pour copier discrètement un fichier .exe sur votre ordinateur. Rassurez-vous ça ne le fera pas chez vous car mon fichier .res n'est pas livré avec le programme. Téléchargez ici un fichier .res contenant toutes les images d'un jeu de cartes de 52 cartes (+ joker + explication). Ultra-pratique.

Ces fichiers se créent grâce à Visual C++. Il servent principalement à stocker d'autres fichiers à "l'intérieur" de notre programme VB. En effet on peut avoir un exécutable qui se trouve dans un dossier, et dans ce même dossier mettre des images ou tout autre type de ressources. Mais si l'on fait ça, cela veut dire que l'utilisateur peut récupérer nos belles images pour les utiliser à ses fins propres. Et on ne veut pas forcément si l'on est égoïste (ou si l'on aime le droit à la propriété). D'autre part, si l'exécutable doit être placé dans le même dossier que d'autres images pour fonctionner, ce n'est pas forcément très pratique non plus, parce qu'il faut l'expliquer aux utilisateurs, et on n'a pas non plus envie de créer un Setup pour tout.

Création d'un fichier .res

Utilisation d'un fichier .res depuis VB

Faites clic droit sur votre projet dans l'arborescence d'en haut à droite, et Add file, puis sélectionnez votre fichier .res tout beau tout neuf. Faites :

Dim Data() as Byte
Data = LoadResData("logiexe", "EXE")
ou Data = LoadResData("fichier2", "DOSSIER1")
pour les images : Set Me.Picture1.Picture = LoadResPicture("image1", "BMP")

où vous l'avez compris, image1 peut très bien être une jpg même si elle est dans le dossier "BMP"

Jouer de la musique

Il y a encore une fois 2 manières : avec Direct Sound (cf la source de Phoenix), ou avec un objet spécial (que l'on insère donc en faisant Ctrl+T, ou Projet->Composants) : le Microsoft Multimédia Control. J'en met un sur ma form par musique ou son que je voudrais jouer. Il faut renseigner 'Filename' (le .wav ou .mp3 (jamais essayé)) et on peut mettre en Visible=False pour cacher un peu. Attention, si ce n'est pas fait, ce n'est pas parce qu'on clique sur le bouton Play que ça va marcher. Idem avec Pause, ... !!! C'est à nous de gérer l'événement "clic sur le bouton ||", "clic sur |>", ... Mais ça se fait bien.

A Form_Load : Form1.MMControl1.Command = "Open", Form1.MMControl1.Command = "Play" pour jouer, et Form1.MMControl1.Command = "Prev" pour remettre à 0 la "bande son".

Codes sources et liens

Vous trouverez dans ma page codes sources :

Tous ces exemples n'ont pas étaient faits dans le but de les mettre un jour accolés à un tutoriel, mais dans des situations bien réelles où ils étaient nécéssité. En clair, programmez pour vous faire plaisir, et naturellement vous verrez que vous aurez besoin de maitriser telle ou telle technique, et là vous regarderez sur internet comment ça marche, comment on s'en sert, et après vous aprenez à la maîtriser, ... Et 2 ou 3 ans plus tard vous commencez à devenir un super guerrier de l'utilisation de VB parce que vous avez acquis sans vous en rendre compte un nombre incroyable de bagages techniques, qui vous permettent de faire à peut près tout.

Laissez un commentaire !

Pas besoin de vous connecter, commencez à taper votre nom et une case "invité" apparaîtra.

Programmation
Tutoriels et conseils pour bien démarrer, outils à utiliser
Tutoriel CamL
Bases, astuces et codes sources pour se familiariser avec ce language
Tutoriel Java
Bases pour créer une appli en Java avec interface + astuces et code sources
Tutoriel AppleScript
Les bases pour créer une belle app en AppleScript + astuces et codes sources
Kyoto - Karaoké !
I believe I can fly
Délation policière
Mise en place par la police d'un système Internet de délation anonyme, dans l'Essonne
2 semaines en Toscane, que bella !
Ti amo Italia y pasta y mozzarella
Jussieu
Arghh
Config MAMP : php/mysql/phpmyadmin sur Mac OS X
MAMP on OS X, in French and English
4 jours en Alaska
Je m'en irai dormir dans le paradis blanc
Le concert
Un film qui vient de sortir. Pronostic ?
Kyoto - la vie ici
train train quotidien en famille d'accueil
Super Cluedo
devenez détective
iPhone 6 moins
Précommandez le vite !!
Driving license
Un pas de plus vers la citoyenneté américaine !
Iles Galapagos
Envie de lézarder
Premiers pas au Japon
Début de mon voyage linguistique
Tokyo !!
arrivée à Tokyo
Shenzhen BackStreets
La Chine qu'on ne visite pas
La maison des tartes
Une de mes petites adresses, rue mouffetard
J'ai essayé le kitesurf strapless
Impressions et conseils
Troubleshooting with iOS. UI tips
Cocoa tips