REAL-WORLD COMPUTER PROGRAMMING FOR KIDS
STEP 28: STEPS ON STEPPING (and BREAKPOINT BONANZA), Part 4
So our app is still dealing with the last question just as it does with all the others, when it’s supposed to be dealing with the last question differently -- as it is the last question and is a special case.
Let’s break it down: What do we want it to do, exactly?
As soon as the last answer is selected, we want it to “freeze” the Radio Buttons (set their Enabled property to false) so that the selection made “sticks” and cannot be changed. Also, different than is the case with other questions, do not enable the “Next” Button on selecting an answer. Also, update the Percentage Label (lblPercentage) to reflect whether the answer selected was correct or not.
What should trigger all this to happen is the State of the freezeAnswer bool variable. It is set in the Event Handler shared by all three Radio Buttons, radioButton1_CheckedChanged:
freezeAnswer = MultipleChoiceClassIndex == numberOfQuestions;
if (freezeAnswer)
{
UpdatePercentageLabel();
btnNext.Enabled = false;
radioButton1.Enabled = false;
radioButton2.Enabled = false;
radioButton3.Enabled = false;
return;
}
So we need to verify that MultiChoiceClassIndex is 10 (so that it equals the number of Questions in the CSV file) when the last answer is selected.
Before we tackle that (by Desk Checking the code): Do you remember when I said that taking a break can give you “new eyes,” so to speak, when you come back and take a fresh look at the code? After taking a break, I noticed this line of code following the “if (freezeAnswer)” block of code shown above:
btnNext.Enabled = (sender as RadioButton).Checked;
Do you see what’s happening? If “freezeAnswer” is true, we disable btnNext. But then we turn right around and enable it again with that line of code, because that line of code runs whether freezeAnswer is true or false. So, this change should solve the problem with btnNext being enabled after selecting the final answer:
if (freezeAnswer)
{
UpdatePercentageLabel();
btnNext.Enabled = false;
radioButton1.Enabled = false;
radioButton2.Enabled = false;
radioButton3.Enabled = false;
return;
}
else
{
btnNext.Enabled = (sender as RadioButton).Checked;
}
Do you see what’s happening? By adding an “else” block, btnNext is only Enabled if freezeAnswer is false.
So that should fix that problem. But what about the RadioButtons not being disabled, and the Percentage Label not being updated?
Let’s Desk Check the code ...
Looking at the code anew, this:
if (questionsAnswered == (numberOfQuestions))
{
questionsAnswered++;
MultipleChoiceClassIndex++;
}
...doesn’t really seem logical anymore. I don’t recall what my mindset was at the time I wrote it, but if you have already answered all the questions (which is by definition the case when the “if” statement equates to true), why would you then increment those two variables? You would be saying, “if questionsAnswered is 10 and numberOfQuestions is also 10, then increment questionsAnswered to 11 and increment MultipleChoiceClassIndex, also.” Why would we want to do that?
Sometimes when Desk Checking your own code, you may feel like Dr. Jekyll and Mr. Hyde – as if you suffer from MPD (Multiple Personality Disorder) and are sometimes the bad coder (Dr. Jekyll) and other times the good coder (Mr. Hyde).

That’s why programmers sometimes blame mistakes in their code on their “evil twin.”
And then the next line of code is:
freezeAnswer = MultipleChoiceClassIndex == numberOfQuestions;
This is not necessarily wrong, but wouldn’t it be more straightforward as:
freezeAnswer = questionsAnswered == numberOfQuestions;
?
So, I changed the code to this:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (questionsAnswered == (numberOfQuestions-1))
{
questionsAnswered++;
}
freezeAnswer = questionsAnswered == numberOfQuestions;
. . .
So now, after the user has selected an answer with the 10th question displaying, the code in effect says, “If 9 questions have already been answered and there are 10 questions, this one is the last one.” And then the questionsAnswered int variable is incremented to 10, and freezeAnswer is true if 10 = the number of Questions (which in this case it is).
Also, the line that incremented the MultipleChoiceClassIndex variable has been taken out, because we won’t be referencing MultipleChoiceClassIndex any more.
So with a Breakpoint on the first line (the “if” statement), we run it again. And ... Dr. Jekyll has been wreaking havoc again.
It seems that the MultipleChoiceClassIndex variable is not holding the expected value. I stepped into a spot where it was 9 (which equates to the last question), but I hadn’t yet reached the last question.
When you “Desk Check” your code, you are stepping through it virtually; you are stepping through it in your mind. So let’s do that with the code above.
I searched for every place in the code where MultipleChoiceClassIndex is assigned a value. There are three places:
First, it is set to -1 when the app starts:
int MultipleChoiceClassIndex = -1;
Then (second and third), in the UpdateTheForm method, MultipleChoiceClassIndex is conditionally set to 0 (from -1) the first time it is called, and incremented thereafter (those lines are italicized):
private void UpdateTheForm()
{
if (MultipleChoiceClassIndex == -1)
{
MultipleChoiceClassIndex = 0;
}
btnNext.Enabled = false;
try
{
if (progressBar1.Value < progressBar1.Maximum)
{
progressBar1.Value = questionsAnswered * stepAmount;
}
if (MultipleChoiceClassIndex < numberOfQuestions)
{
if (questionsAnswered > 0)
{
UpdateQuestionNumberLabel();
UpdatePercentageLabel();
MultipleChoiceClassIndex++;
}
. . .
This logic works because “questionsAnswered > 0” is only true after the first time through. So the first time through, the value of MultipleChoiceClassIndex starts at -1 and becomes 0, and then populates the form with the first set of Question and Candidate Answers (at index 0 in the List); and then subsequent times through this code, MultipleChoiceClassIndex is incremented, and the next set of Question and Candidate Answers are populated onto the form -- incrementing from 0 to 1, the next time from 1 to 2, etc.
The app only starts once, and that first assignment (initializing the value of MultipleChoiceClassIndex to -1) works fine. So the problem must be in the UpdateTheForm method. So I put a Breakpoint on the first line in this section of the UpdateTheForm method:
if (questionsAnswered > 0)
{
UpdateQuestionNumberLabel();
UpdatePercentageLabel();
MultipleChoiceClassIndex++;
}
...and ran the app again.
While doing that, I eventually discovered that the problem is not in the middle of the UpdateTheForm method where I had put the Breakpoint, but on the first line of the RadioButtons’ shared CheckedChanged Event Handler:
if (questionsAnswered == (numberOfQuestions-1))
{
questionsAnswered++;
}
freezeAnswer = questionsAnswered == numberOfQuestions;
Why was that the problem area? Because after answering the 9th question (and thus the value of the questionsAnswered variable is 9), the “if” test equates to true and thus increments questionsAnswered to 10 (even though only 9 questions have been answered so far, not 10).
The fact that questionsAnswered is now 10 causes freezeAnswer to be set to true below it (since 10 = 10), and the 10th answer cannot be chosen because the RadioButtons are “frozen” -- they don’t allow clicking.
Note: Something that can be confusing is the statement made in the previous paragraph, namely “since 10 = 10.” For people without programming knowledge, it’s probably plain that it’s saying, “since ten equals ten” (and that is what I meant). But now that you know C#, you may read that as “since ten becomes ten” instead, which doesn’t make a whole lot of sense. That is, in C#, the equals sign (=) means “becomes” or “is assigned” or “gets,” whereas (in C#) the double equals sign (==) means “equals” or “is the same as.”
So the moral of that tale is that we do need to keep context in mind to determine when English is being used (human speech) and when C# is being used (a hybrid human/computer lingo).
It is somewhat similar with numbers. When we name an int[eger] variable something like questionsAnswered, we are storing cardinal numbers in there, such as 0,1,2,3, etc. But when we speak of the index into a set of numbers like that, and we use ordinal numbers, like “the first item” it can get confusing, because the first item is at index 0, not 1.
So ...

We normally increment questionsAnswered only after the user “locks in” their answer by selecting the “Next” Button, but on the last question we want to prevent them from being able to select the “Next” Button after they have made a selection from the Radio Buttons. So, we need to determine where the questionsAnswered variable is updated. It turns out that there are only two places: On the first line of the problematic code shown above (in the RadioButtons’ CheckedChanged Event Handler), and in the “Next” Button’s Click event:
if (questionsAnswered < numberOfQuestions)
{
questionsAnswered++;
}
Since we coded the RadioButtons’ CheckedChanged Event Handler to act similar to the “Next” Button’s Click Event Handler when on the last question, what we seem to be missing is the call to the UpdateTheForm method that the latter calls.
So the questions we need to ask ourselves now are:
1) Would calling the UpdateTheForm method from the RadioButtons’ CheckedChanged Event Handler do what we want?
-and:
2) How can we determine that the last Question is being displayed on the Form when the RadioButtons’ CheckedChanged Event Handler fires?
So let’s look at the UpdateTheForm method ... [ look, look, look ] ...On second thought, it seems like the only thing that method does that we also want to do in the CheckedChanged Event Handler during the last question is to call the UpdatePercentageLabel method, and we’re already doing that there.
So apparently the crux of the matter (not the “biscuit” -- which is British-ese* for “cookie”) is getting the RadioButtons’ CheckedChanged Event Handler to cause the freezeAnswer variable to equate to true at the appropriate time.
* British-ese is a quaint dialect of American.
I changed the code to this:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
if (!(sender as RadioButton).Checked)
{
return;
}
if (questionsAnswered == (numberOfQuestions - 1))
{
questionsAnswered++;
}
freezeAnswer = questionsAnswered == numberOfQuestions;
if (freezeAnswer)
. . .
Note: If you find it easier to understand that way, you can change the assignment to freezeAnswer to put the two variables being compared in parentheses, like so:
freezeAnswer = (questionsAnswered == numberOfQuestions);
Either way (with or without the parentheses) works the same, but it may be more understandable to you with them. YMMV.
It works pretty well now. I do have to add the “nicety” (or gingerbreading) of having the Progress Bar be maxed out after the last question (rather than be left at 90% of the way across), but that's not a huge deal. So there is something else that the “Next” Button’s Click Event Handler was doing that we want to do here, too (update the Progress Bar).
Let’s go ahead and fix that, while we’re thinking about it, rather than putting it off. We add this snippet of code from the UpdateTheForm method to our CheckedChanged code:
if (progressBar1.Value < progressBar1.Maximum)
{
progressBar1.Value = questionsAnswered * stepAmount;
}
On second thought, now that it’s being called from two places (the UpdateTheForm method and the CheckedChanged Event Handler), let’s make a method out of it, and call the method from those two places .
Create the method like so:
private void UpdateProgressBar()
{
if (progressBar1.Value < progressBar1.Maximum)
{
progressBar1.Value = questionsAnswered * stepAmount;
}
}
...and then call that method from both places, rather than duplicating the same code in two spots. Replace the code in the UpdateTheForm method with a call to the new method and change the CheckedChanged Event Handler to be:
if (freezeAnswer)
{
UpdateProgressBar();
UpdatePercentageLabel();
. . .
Now it acts and looks just as we expect it to:

Note: Breaking the code that is used more than once out into its own separate method is sensible because if the code ever needs to change (be refactored), you only have to do it in one place. If you duplicate code, you may forget to update it in one place or another. This goes along with the DRY (Don’t Repeat Yourself) Programming Principle. We will talk more about this when we get into the next main portion of our Journey, on Database Design and Usage.
So as for the bigger win (not just the cosmetic win just now displayed): What was the difference in the new code that caused it to work? You might notice the only change was something added at the very beginning of the code:
if (!(sender as RadioButton).Checked)
{
return;
}
That is there because, when adding a conditional Breakpoint to that code, I noticed that I reached the “hit count” twice as soon as I was expecting to, and some googling informed me that the RadioButton’s CheckedChanged Event Handler is called/triggered not only when a RadioButton is checked, but also when it is unchecked. So I added the code above to ignore the rest of the code if it’s an “uncheck” that fired the handler (since we don’t need to respond to RadioButtons being unchecked). It’s a somewhat arcane solution, yes, and once in awhile it will be something like that that is causing problems. Googling is your friend.
However (here we go again), when I selected the next CSV file from the list, I got this:

What do you think? Would that make a good topic for the next Step on our Journey into the Wonderful World of Coding?
So do I; on that note, then -- Until then!
The first volume of my four-volume series, Real-World Computer Programming for Kids of All Ages, namely Volume 1: Windows Forms Apps Using C# and Visual Studio is now available in both Paperback and Kindle formats. Volume 1 contains everything that has been printed in this Newsletter so far and through Step 34.
The kindle version of the book can be accessed here: https://www.amazon.com/dp/B08H7DKKCS/ and the Paperback version can be accessed here https://www.amazon.com/dp/B08H9RB1WG/

Earth-shakingly Important Notice: If you have a basic programming question (suitable to an audience of “Kids”), send it to idiolectable@gmail.com, specifying whether you would like your name and location used if it is printed in a future “Step” of this newsletter. If you are a subscriber to the newsletter, you can also leave a question at the bottom of this Step, in the “Comments” section.
If you do not want to give your real name, a nickname is acceptable (the first “Letter to the Editor” of mine that was printed appeared in Rolling Stone magazine, back in the early 1970s, and I signed it “Sylvester” for some reason which I no longer remember).
Finally, it’s always interesting to see where people are from, so please provide your City or Town and the State it’s in, too (or Province, or whatever the region where you live is called).
To listen to this Step, the audio of it can be found here: