Niveau débutant
Réalisation d'un QCM sous VBA Excel, par l'intermédiaire d'un UserForm.
Introduction
Cette fiche pratique fait suite à celle-ci :
http://www.commentcamarche.net/faq/46730-vba-mon-tout-premier-userform-debutants-extremes
Cette seconde boîte de dialogue va :
- modifier les propriétés dynamiquement (par le code)
- utiliser des informations situées dans la feuille
Pré-requis : savoir utiliser et comprendre les blocs With/End With.
La "base de données" utile (les questions/réponses) pour ce QCM ont été déjà préparées pour vous dans
ce fichier à télécharger gratuitement!!!
Mode création
En mode création, commencez par créer un UserForm avec les contrôles suivants :
- un Label,
- un Frame contenant 3 OptionButton,
- un CommandButton.
Si vous n'êtes pas familier avec la boîte à outils, vous pouvez jeter un oeil ici :
http://www.commentcamarche.net/faq/46746-vba-userforms-boite-a-outils-controles
Ne changez aucune des propriétés (taille, Name, Caption, etc) de ces contrôles.
Contentez vous de les placer et d'aligner les 3 OptionButtons.
Vous obtenez quelque chose comme ceci :
Basculons maintenant en mode "code", soit par un double-clic dans le fond de l'UserForm, soit par un clic droit/Code.
Deux lignes apparaissent alors :
Private Sub UserForm_Click()
End Sub
Les propriétés par le code
Au lancement de l'userform, je souhaite obtenir la configuration initiale suivante :
- avoir un texte indiquant à l'utilisateur que, pour commencer, il convient de cliquer sur le bouton,
- ne pas voir apparaitre les boutons d'option (le QCM n'étant pas encore commencé)
- et avoir un bouton qui "dit" : COMMENCEZ LE TEST
Il nous faut également régler, pour chaque contrôle :
- les marges gauche et haut,
- la largeur et la hauteur,
- le texte.
Nous pourrions avoir besoin, au cours de la "durée de vie" de notre UserForm, de rappeler cette configuration initiale des contrôles.
Nous allons donc créer une procédure réservée à cet effet, au sein de notre module de code de l'userform.
Procédure que nous pourrons appeler quand bon nous semble.
Copiez/collez ce code dans le module de code de l'userform :
Private Sub Config_Initiale_Controles()
'Se rapporte à l'objet : UserForm1
With UserForm1
.Width = 350 'largeur
.Caption = "QCM" 'titre
'se rapporte à l'objet Label1 de l'UserForm1
With .Label1
.Caption = "Pour commencer, cliquez sur le bouton : COMMENCEZ LE TEST" 'texte à afficher
.Left = 5 'marge de gauche par rapport au bord gauche de l'userform
.Top = 5 'marge de haut par rapport au bord supérieur de l'userform
.Width = UserForm1.Width - 15 'largeur
.Height = 50 'hauteur
End With
With .Frame1
.Visible = False 'rend non visible le frame ET son contenu
.Caption = "Réponses possibles : "
.Left = 5 'marge de gauche par rapport au bord gauche de l'userform
.Top = Label1.Top + Label1.Height + 5 'marge de haut par rapport au bord inférieur du Label
.Width = UserForm1.Width - 15 'largeur
.Height = 90
With .OptionButton1
.Caption = ""
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = 5 'marge de haut par rapport au bord supérieur du Frame
.Width = Frame1.Width - 15
.Height = 20
End With
With .OptionButton2
.Caption = ""
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = OptionButton1.Top + OptionButton1.Height + 5 'marge de haut par rapport au bord inférieur de l'OptionButton1
.Width = Frame1.Width - 15
.Height = 20
End With
With .OptionButton3
.Caption = ""
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = OptionButton2.Top + OptionButton2.Height + 5 'marge de haut par rapport au bord inférieur de l'OptionButton2
.Width = Frame1.Width - 15
.Height = 20
End With
End With
With .CommandButton1
.Caption = "COMMENCEZ LE TEST"
.Left = 5
.Top = Frame1.Top + Frame1.Height + 5 'marge de haut par rapport au bord inférieur du Frame
.Width = UserForm1.Width - 15
.Height = 30
End With
.Height = .CommandButton1.Top + .CommandButton1.Height + 30 'hauteur de l'userform en fonction des hauteurs des contrôles
End With
End Sub
Ici, rien de spécial à expliquer, les propriétés sont indiquées dans les commentaires.
Cette image vous montre simplement la méthode de placement des contrôles les uns par rapport aux autres :
Il nous faut, maintenant, appeler cette Sub avant l'affichage de l'UserForm.
Pour cela, nous pouvons utiliser, à notre convenance, soit l'événement Initialize, soit l'événement Activate de l'UserForm.
Nous opterons ici pour Initialize.
Dans le menu "Général", choisir UserForm, dans le menu "événements", choisir Initialize.
Vous obtenez ces lignes de code supplémentaires :
Private Sub UserForm_Click()
End Sub
Private Sub UserForm_Initialize()
End Sub
Note : N'ayant pas besoin de l'événement Click, vous pouvez, maintenant, supprimer les lignes qui s'y rapportent.
Pour appeler une procédure depuis une autre, j'ai pris l'habitude d'utiliser : Call.
Ce n'est pas obligatoire, mais je trouve cela plus explicite.
Donc, dans notre événement Initialize, appelons notre Sub Config_Initiale_Controles :
Private Sub UserForm_Initialize()
Call Config_Initiale_Controles
End Sub
Vous pouvez tester (F5) en changeant la propriété visible du Frame (True/False) pour voir le résultat.
De la feuille vers l'userform
De part notre choix initial, le label va contenir la question et les boutons d'option les réponses, respectivement :
- OptionButton1 : réponses de la colonne B,
- OptionButton2 : réponses de la colonne C,
- OptionButton3 : réponses de la colonne D.
Puisque nous connaissons les colonnes, il va nous falloir, pour chercher l'information, les numéros de ligne.
Pour cela, nous allons créer une variable de "niveau module" (dont la durée de vie = durée d'affichage de l'userform).
Tout en haut (en entête et avant toute déclaration de Sub), écrivez donc :
Dim Ligne As Byte
Il va nous falloir maintenant, créer une procédure qui va :
- chercher l'information dans la cellule : Ligne, Colonne de l'optionButton
- inscrire cette information (la réponse) dans le Caption de l'OptionButton concerné.
En dessous de votre procédure Config_Initiale_Controles (après son End Sub), copiez-collez ce code :
Private Sub Questions_Reponses()
With Sheets("Feuil1")
'Question
'on modifie la propriété Caption du label
'avec le contenu de la colonne A (Questions)
Label1.Caption = .Range("A" & Ligne).Value
'réponses
'on s'assure que l'option est décochée
OptionButton1.Value = False
'on complète sa propriété Caption avec le contenu de la cellule concernée
OptionButton1.Caption = .Range("B" & Ligne).Value
'Idem pour les 2 autres
OptionButton2.Value = False
OptionButton2.Caption = .Range("C" & Ligne).Value
OptionButton3.Value = False
OptionButton3.Caption = .Range("D" & Ligne).Value
End With
End Sub
Les explications nécessaires sont données en commentaires du code.
Pour tester, il va falloir nous occuper de l'événement Click() sur le CommandButton.
Pour cela, dans le menu "général" choisir : CommandButton1.
Par défaut, VBA vous place dans l'événement Click() par ces lignes :
Private Sub CommandButton1_Click()
End Sub
Voici donc maintenant, dûment commenté, le code de cet événement :
Private Sub CommandButton1_Click()
'premier clic ,sur le bouton, on consulte sa propriété Caption
'si "COMMENCEZ LE TEST" Alors
If CommandButton1.Caption = "COMMENCEZ LE TEST" Then
'on modifie la propriété Caption du bouton pour les prochains clics
CommandButton1.Caption = "VALIDER"
'la première question étant ligne 2 dans la feuille :
Ligne = 2
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
'Affichage du Frame (qui était jusqu'alors invisible)
Frame1.Visible = True
Else
'cas des clics suivants (à partir du second)
'on passe à la ligne suivante
Ligne = Ligne + 1
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
End If
End Sub
Vous pouvez maintenant tester, votre UserForm affiche bien les questions/réponses de votre feuille Feuil1 dans ses contrôles.
Finalisation
Le but de ce tutoriel n'est pas de réaliser un QCM, mais de comprendre comment charger des informations de la feuille vers l'userform.
Cependant, le travail étant quasiment achevé, il aurait été dommage de ne pas finaliser notre QCM.
Le souci maintenant va être de compter les points, puisque l'on peut passer une question sans y répondre.
Pour cela, créons une seconde variable de niveau module, en inscrivant juste sous Dim Ligne As Byte :
Dim Points As Byte
Puis, nous allons créer une Function qui aura pour but de renvoyer 1 si la réponse est bonne 0 sinon.
Pour cela, il suffit de vérifier quel option a été choisie, et de regarder si la cellule relative est de couleur jaune (6).
Soit :
Private Function Correction() As Byte
'Valeur par défaut
Correction = 0
With Sheets("Feuil1")
'si la propriété Value de l'optionbutton = true signifie
'que vous avez coché cette réponse
'donc on vérifie, dans la feuille, si la cellule colonne B est bien en jaune à la Ligne concernée
If OptionButton1.Value = True And .Range("B" & Ligne).Interior.ColorIndex = 6 Then
Correction = 1 'si oui, correction renvoie 1
'Idem
ElseIf OptionButton2.Value = True And .Range("C" & Ligne).Interior.ColorIndex = 6 Then
Correction = 1
ElseIf OptionButton3.Value = True And .Range("D" & Ligne).Interior.ColorIndex = 6 Then
'Idem
Correction = 1
End If
End With
End Function
Enfin, modifions le code de notre bouton, comme ceci :
Private Sub CommandButton1_Click()
'premier clic ,sur le bouton, on consulte sa propriété Caption
'si "COMMENCEZ LE TEST" Alors
If CommandButton1.Caption = "COMMENCEZ LE TEST" Then
'on modifie la propriété Caption du bouton pour les prochains clics
CommandButton1.Caption = "VALIDER"
'la première question étant ligne 2 dans la feuille :
Ligne = 2
Points = 0
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
'Affichage du Frame (qui était jusqu'alors invisible
Frame1.Visible = True
Else
'cas des clics suivants (à partir du second)
'compte les points
Points = Points + Correction
'on passe à la ligne suivante
Ligne = Ligne + 1
'teste si dernière question
If Ligne = 22 Then
'affichage du nombre de points
MsgBox "Vous avez obtenu : " & Points & " sur 20."
'remise à 0 de la configuration => retour au départ
Call Config_Initiale_Controles
'on quitte la procédure pour ne pas déclencher le Call Questions_Reponses
Exit Sub
End If
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
End If
End Sub
Nous avons maintenant un code fonctionnel. Ne vous reste plus qu'à tester votre UserForm et... vos connaissances!
Code complet de l'UserForm
Juste pour vérification...
Option Explicit
Private Ligne As Byte
Private Points As Byte
Private Sub CommandButton1_Click()
'premier clic ,sur le bouton, on consulte sa propriété Caption
'si "COMMENCEZ LE TEST" Alors
If CommandButton1.Caption = "COMMENCEZ LE TEST" Then
'on modifie la propriété Caption du bouton pour les prochains clics
CommandButton1.Caption = "VALIDER"
'la première question étant ligne 2 dans la feuille :
Ligne = 2
Points = 0
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
'Affichage du Frame (qui était jusqu'alors invisible
Frame1.Visible = True
Else
'cas des clics suivants (à partir du second)
'compte les points
Points = Points + Correction
'on passe à la ligne suivante
Ligne = Ligne + 1
'teste si dernière question
If Ligne = 22 Then
'affichage du nombre de points
MsgBox "Vous avez obtenu : " & Points & " sur 20."
'remise à 0 de la configuration => retour au départ
Call Config_Initiale_Controles
'on quitte la procédure pour ne pas déclencher le Call Questions_Reponses
Exit Sub
End If
'on appelle la procédure d'affichage de la question
Call Questions_Reponses
End If
End Sub
Private Sub UserForm_Initialize()
Call Config_Initiale_Controles
End Sub
Private Sub Config_Initiale_Controles()
'Se rapporte à l'objet : UserForm1
With UserForm1
.Width = 350 'largeur
.Caption = "QCM" 'titre
'se rapporte à l'objet Label1 de l'UserForm1
With .Label1
.Caption = "Pour commencer, cliquez sur le bouton : COMMENCEZ LE TEST" 'texte à afficher
.Left = 5 'marge de gauche par rapport au bord gauche de l'userform
.Top = 5 'marge de haut par rapport au bord supérieur de l'userform
.Width = UserForm1.Width - 15 'largeur
.Height = 50 'hauteur
End With
With .Frame1
.Visible = False 'rend non visible le frame ET son contenu
.Caption = "Réponses possibles : "
.Left = 5 'marge de gauche par rapport au bord gauche de l'userform
.Top = Label1.Top + Label1.Height + 5 'marge de haut par rapport au bord inférieur du Label
.Width = UserForm1.Width - 15 'largeur
.Height = 90
With .OptionButton1
.Caption = "...................."
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = 5 'marge de haut par rapport au bord supérieur du Frame
.Width = Frame1.Width - 15
.Height = 20
End With
With .OptionButton2
.Caption = "...................."
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = OptionButton1.Top + OptionButton1.Height + 5 'marge de haut par rapport au bord inférieur de l'OptionButton1
.Width = Frame1.Width - 15
.Height = 20
End With
With .OptionButton3
.Caption = "...................."
.Left = 5 'marge de gauche par rapport au bord gauche du Frame
.Top = OptionButton2.Top + OptionButton2.Height + 5 'marge de haut par rapport au bord inférieur de l'OptionButton2
.Width = Frame1.Width - 15
.Height = 20
End With
End With
With .CommandButton1
.Caption = "COMMENCEZ LE TEST"
.Left = 5
.Top = Frame1.Top + Frame1.Height + 5 'marge de haut par rapport au bord inférieur du Frame
.Width = UserForm1.Width - 15
.Height = 30
End With
.Height = .CommandButton1.Top + .CommandButton1.Height + 30 'hauteur de l'userform en fonction des hauteurs des contrôles
End With
End Sub
Private Sub Questions_Reponses()
With Sheets("Feuil1")
'Question
'on modifie la propriété Caption du label
'avec le contenu de la colonne A (Questions)
Label1.Caption = .Range("A" & Ligne).Value
'réponses
'on s'assure que l'option est décochée
OptionButton1.Value = False
'on complète sa propriété Caption avec le contenu de la cellule concernée
OptionButton1.Caption = .Range("B" & Ligne).Value
'Idem pour les 2 autres
OptionButton2.Value = False
OptionButton2.Caption = .Range("C" & Ligne).Value
OptionButton3.Value = False
OptionButton3.Caption = .Range("D" & Ligne).Value
End With
End Sub
Private Function Correction() As Byte
'Valeur par défaut
Correction = 0
With Sheets("Feuil1")
'si la propriété Value de l'optionbutton = true signifie
'que vous avez coché cette réponse
'donc on vérifie, dans la feuille, si la cellule colonne B est bien en jaune à la Ligne concernée
If OptionButton1.Value = True And .Range("B" & Ligne).Interior.ColorIndex = 6 Then
Correction = 1 'si oui, correction renvoie 1
'Idem
ElseIf OptionButton2.Value = True And .Range("C" & Ligne).Interior.ColorIndex = 6 Then
Correction = 1
ElseIf OptionButton3.Value = True And .Range("D" & Ligne).Interior.ColorIndex = 6 Then
'Idem
Correction = 1
End If
End With
End Function
Conclusion
Téléchargez le fichier complété
J'y ai inséré un bouton de lancement et modifié une propriété supplémentaire à un contrôle...
Mais, lequel est-ce ?
Lors de ces deux fiches pratiques, nous avons vraiment repris tout depuis le tout début.
Il va nous falloir maintenant nous attacher à la création d'un userform réalisant de vrais échanges avec la feuille.
Le prochain tuto vous permettra de construire un Formulaire de :
- saisie dans l'userform
- enregistrement dans la feuille
- modification (import de la feuille vers l'userform, modifications et export de l'userform vers la feuille)
- recherche
Le B-A BA donc des formulaires de saisie.