Multiple Choice
Up Multiple Choice 2 Multiple Choice 3
Now let's make a serious program that could be genuinely useful in a school context - it could even help you get better grades in different subjects! We will make a multiple choice testing program that automatically grades the user's responses. VB can handle multiple choice responses very easily, but would be very bad at grading essays.

In this project you will learn:

bulletmore about control arrays
bullethow to declare constants
bulletmore about arrays, include multi-dimensional arrays and dynamic arrays

This project is in two parts - follow the link above to move to the second part.

Make a new project and set up the form with 2 labels, 2 command buttons and 4 option buttons like this:

Set the font property of all the controls to size 10, except that the top three controls can be set to size 14. Note that the form's BorderStyle property has been set to 'fixed single' so that the user cannot resize the form. Set names as follows:

bulletsmall label: lblQno
bulletcommand buttons: cmdPrev and cmdNext
bulletlarge label: lblQuestion
bulletoption buttons: optAns

Giving all the option buttons the same name turns them into a 'control array' which has the advantage that they can share the same code. VB can still distinguish between the members of the control array because each member is given a unique index value (0 to 3 in this case). Thus the top option button is optAns(0) and so on. An easy way to create the control array is to make the first option button (giving it the name optAns), copy it then paste it several times, answering 'yes' when VB asks you whether you wish to create a control array.

It will be easier for us to write the code if the index numbers of the option buttons run from 1 to 4 rather than 0 to 3 so change the index property values, starting by changing the index property of the bottom option button from 3 to 4 (why can't you start at the top?)

We could put each question on a separate form (it's OK to have several forms in the same project) but this would probably be a bad idea for we would have to deal with several forms, many controls etc. and if we decide to change the layout we will have to modify each form individually. So we will use a single  form and use the command buttons to switch between different questions, displaying different question/answer sets as appropriate. We'll keep the questions and answers in arrays in the program's code (remember that an array is just a variable that can contain several values instead of just one).

Now for the code - let's start with the command buttons that move us from question to question. Let's make a variable qno to keep track of which question we are looking at. Since the total number of questions will not change while the program is running, we will create a 'constant' called numq to record the total number of questions in the test. Notice how the constant is set up in the code below, and notice that it is not necessary to declare a constant with a Dim statement. Let's also create an array to store the questions, called question(). The empty parentheses in this declaration create a 'dynamic array' - one that can contain as many elements as needed. We will need to use the Redim statement later on to tell the computer how many elements will actually be present (this number will equal numq). We'll also need a dynamic array Answer() to store all the answers for the various questions, an array called correct() to store the number of the answer which we know to be correct for each question and an array called response() to store the choices made by the person being tested. So far our code looks like this (it belongs in the General Declarations area, above all other code and outside every procedure):

Dim qno, question(), ans(), correct(), response()
Const numq = 4    ' total number of questions 

Let's think about the code for the cmdNext button. When we click it, we want to see the question and answers corresponding to the next question, so the number of the current question needs to be increased by one and then the corresponding questions and answers need to be displayed, so we could use this code:

Private Sub cmdNext_Click()
qno = qno + 1
SetQuestion
End Sub

If you were to run the program now, you would get an error message 'Sub or Function not defined' for  VB does not have a SetQuestion statement built in, and therefore this line generates a SetQuestion event that has no procedure yet to handle it. Soon we will make the SetQuestion procedure.

Before we do the SetQuestion procedure, let's do the cmdPrev code - it's just the same as the cmdNext code except that we will subtract one from 'qno' rather than adding to it:

Private Sub cmdPrev_Click()
qno = qno - 1
SetQuestion
End Sub

Let's try to build up this program one small step at a time - let's write a SetQuestion procedure that doesn't actually set the question yet but simply sets the caption of the lblQno label to show the current question number:

Private Sub SetQuestion()
lblQno.Caption = qno
End Sub

Run the program and notice two things:
bulletthe question number has no limits - it can go very high, or become zero or negative. This is to be expected at this stage.
bulletif you click the cmdNext button first, the label's caption remains at 1 for the first click

The second problem occurs because when the qno variable was created with Dim qno, it was given the value of zero initially, so clicking the cmdNext button adds 1 to the zero and displays the answer, 1, in the label's caption, where a 1 was already on display. Let's fix the second problem by telling the program to set qno to equal 1 when the program starts. When any program starts, the first thing that happens is that the form is loaded, generating a Form_Load event. Therefore code put in the Form_Load procedure will be executed before all other code:

Private Sub Form_Load()
qno = 1
End Sub

With the new code in place, the second problem noted above has been solved.

Now let's add to the Form_Load procedure some code to 'dimension' (set the size of) the arrays as the program starts:

Private Sub Form_Load()
ReDim question(numq), ans(numq, 4), correct(numq), response(numq)
FillArrays
qno = 1
SetQuestion
End Sub

The ReDim statement sets the size of the dynamic arrays created earlier. Actually the arrays will have space for 5 values, not 4, for the arrays begin with element zero, not one.  Thus ReDim question(numq), for example, creates an array with values question(0) to question(4) - that's 5 values in all. We won't be using the first element (element zero) of each array. The following line calls the FillArrays procedure which we have not yet written but which will fill the arrays with the questions and answers stored elsewhere in the code. The SetQuestion line is included so that the first question (qno=1) is displayed as soon as the program starts.

There's something special going on in the above procedure, in the ReDim line - it's the way the ans array has been dimensioned (sized). The ans array has to contain four different answers for each question, so the array has been created as a two-dimensional array with space for several answer values for each question. We have actually created space for 5 answers for each question, with index values from 0 to 4, but we will only use the index numbers 1 to 4 - we won't be using the value with an index number of zero.

Let's address the other problem mentioned above: the command buttons allow us to increase or decrease the question number without limits, so we can have negative question numbers, for example. The neatest way to limit the range of numbers that can be reached with the command buttons is to disable each one when it would otherwise allow us to access an invalid question number. In other words, the cmdPrev button should be disabled whenever question one is displayed, and the cmdNext button should be disabled whenever the last question (with qno equal to numq) is displayed. We could code this with an IF structure, but there may be a better way:

Private Sub SetQuestion()
cmdPrev.Enabled = (qno <> 1)
cmdNext.Enabled = (qno <> numq)
lblqno.Caption = qno
End Sub

Look at the first of the highlighted lines above. The '<>' means 'is not equal to, so the expression in parentheses will be true if the question number is not equal to 1, and false if the question number is 1. Thus the complete line enables the cmdPrev button when the question number is not 1 (by setting the enabled property to true), and disables it when the question number is 1 (by setting the enabled property to false). If you try the program, you will notice that the code works fine, but there is still one small problem - the cmdPrev button is enabled when the program starts, so it is possible to move to question number zero. Fix this problem by setting the cmdPrev enabled property to false initially, using the properties window.

Now let's work on the FillArrays procedure that will be called from the Form_Load procedure above. We have already created several arrays with a Dim statement and dimensioned (sized) them with a ReDim statement in the Form_Load procedure, but all the arrays we have created are empty. We now need to fill the question() array with questions, the answer() array with answers, and so on. I hope you will create some original questions of your own, to replace these:

Private Sub FillArrays()
question(1) = "What is the most popular programming language in the world?"
ans(1, 1) = "C++"
ans(1, 2) = "Pascal"
ans(1, 3) = "Visual Basic"
ans(1, 4) = "Java"
correct(1) = 3  ' the correct answer, Visual Basic, 
' has the index value 3 above

question(2) = "Which VB control has no caption property?"
ans(2, 1) = "Textbox"
ans(2, 2) = "Commandbutton"
ans(2, 3) = "Label"
ans(2, 4) = "PictureBox"
correct(2) = 1  ' the correct answer to the second 
' question is answer 1, "textbox"

question(3) = "What would this code display? MsgBox(2 + 2 / 2 + 2)"
ans(3, 1) = "1"
ans(3, 2) = "4.5"
ans(3, 3) = "5"
ans(3, 4) = "An error message"
correct(3) = 3  ' VB will do the division first, then 2+1+2

question(4) = "When a VB program is run, which event happens first?"
ans(4, 1) = "Dim"   ' not an event
ans(4, 2) = "Form_Load"
ans(4, 3) = "cmd1_Click"
ans(4, 4) = "Form_Run"
correct(4) = 2
End Sub

Now that we have set up the questions and answers, we can extend the SetQuestion code so that the appropriate question and answers are displayed each time we move to a new question:

Private Sub SetQuestion()
cmdPrev.Enabled = (qno <> 1)
cmdNext.Enabled = (qno <> numq)
lblqno.Caption = qno
lblQuestion.Caption = question(qno)
For n = 1 To 4
	optAns(n).Caption = ans(qno, n)
Next
End Sub
We didn't really have to use a For...Next loop here but it makes the code a little neater.. and it's good practice!

Now the program allows us to view each of the questions and its corresponding answers. However, if we choose any answer we notice that the computer 'forgets' our choice in the time it takes to move to a different question and then return. We have no code to record the user's choices, nor to set the option buttons when we return to a question that has already been answered.

The neat thing about a control array is that all the members of the array share the same code. Let's write the code for our option buttons which form a control array since they all have the same name (optAns):

Private Sub optAns_Click(Index As Integer)
response(qno) = Index
End Sub

That was short! Note that when we click any option button VB copies its unique index property into a variable called index which we can then use in the code. For example, if we click the top option button, OptAns(1), then the index variable is set equal to 1. The middle line of code copies the value of the index variable into the response array, so that the user's selection (1, 2, 3 or 4) is stored even when we move to a different question.

An annoying aspect of our program is that each time we move to an unanswered question, the first answer is already selected - this could be confusing so let's fix it with the following modification:

Private Sub SetQuestion()
cmdPrev.Enabled = (qno <> 1)
cmdNext.Enabled = (qno <> numq)
lblqno.Caption = qno
lblQuestion.Caption = question(qno)
For n = 1 To 4
	optAns(n).Caption = ans(qno, n)
	optAns(n).Value = False
Next
If response(qno) <> "" Then
	optAns(response(qno)).Value = True
End If
End Sub

When the response array is created it contains empty values rather than zeros. The extra code above first sets the value property of each option button to false then, after checking that the corresponding response value is not empty, sets the value property of one of the option buttons to true, according to the number stored in the response array, making that option button appear selected. Ooof!

As a final step, we should include an additional command button that allows the user to indicate that he or she has finished working on the test and is ready to be GRADED. Add a command button with the caption "End Test" and the name "cmdEndTest" and then, for EXTRA CREDIT, work out code for this button that will use a messagebox to display the user's score or, even better, the user's percentage. Hint: the code should probably use a For...Next loop that moves through all the questions comparing each of the user's choices with the correct answer. Each time a match is found the value of a new variable called score could be incremented (increased) by 1. HAPPY CODING! If, after several hours of blood, sweat and tears you are unable to get this code working then you can click HERE for help. 

The user of your program might want to review the questions after grading, to see which questions were answered wrong. For extra extra credit, add the code to make this possible.

For extra extra extra credit, how about adding sound effects. Do the short project called called Sound to see how sound effects can be added. There could be a sound such as THIS ONE each time we move from one question to another, or applause if the percentage of correct answers is greater than 80, for example... If left-clicking these links does not give you a file-save option then try right-clicking them.

For extra extra extra extra credit, you could..

bulletadd a bunch more questions
bulletadd menus to replace some of the command buttons. You could have, for example, a File menu containing a Grade Test item as well as an Exit item, and a Help menu with an About item. See the Menus project to learn how to do menus.
bulletimprove the program in ways that only YOU have thought of
bulletmodify the program so that some questions could show an appropriate picture or play sounds that are appropriate only for those specific questions. For help with this please see the second part of this project...
Now click HERE to move to learn how to add pictures, sounds and even videos to your project.

Multiple Choice 2 Multiple Choice 3

Previous Up Next