VBA Mon second UserForm - Création d'un QCM

Décembre 2016



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.

A voir également :

Ce document intitulé «  VBA Mon second UserForm - Création d'un QCM  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.