0

When I do this for loop on a windows form app and I click a button to start this for loop, the label just changes to the last item in the array, how do I get it to go through the array one at a time?

for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
    lblScrambled.Text = WordsnQuestions.questions [i++];
}
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • You'll need to add a delay inside that loop – Dai Sep 30 '22 at 21:07
  • 3
    You don't get an `IndexOutOfRangeException` by executing `i++` twice per iteration? Each iteration overwrites `lblScrambled.Text` so its value once the loop completes is that of the last element in `questions`. What different result do you want or expect? – Lance U. Matthews Sep 30 '22 at 21:09
  • What do you want the label to do exactly? That loop will iterate over all elements. – Luke Sep 30 '22 at 21:09
  • Think of it this way: It is looping through the array one at a time. It's just doing it faster than you could possibly see. – Heretic Monkey Sep 30 '22 at 21:09
  • @HereticMonkey Except he isn't giving the UI time to draw the new text either.... – Aron Sep 30 '22 at 21:15
  • @LanceU.Matthews He is using i++. If he was using `WordsnQuestions.questions [++i]` that would give an `IndexOutOfRangeException`. – Aron Sep 30 '22 at 21:17
  • @Aron Tomato/tomahto. I was saying that the computer is looping too fast to see the change in text (not really, because the UI doesn't actually try, hence the "Think of it this way"). You're saying that he's not giving the UI time to show the text. Same result. – Heretic Monkey Sep 30 '22 at 21:19
  • @Aron Good point. So then the issue is not trying to read past the end of the collection but skipping elements within it (which it's unknown if that's intentional or not). – Lance U. Matthews Sep 30 '22 at 21:20
  • This issue is **both**: it's skipping past every other iteration, **and** the loop is too fast. But it's worse than just too fast too see it: the code never yields back to the event loop, so windows will **never even try** to draw the earlier iterations. By the time it processes paint events again it's already finished and set to the final value. – Joel Coehoorn Sep 30 '22 at 21:56

2 Answers2

1

The loop is too fast... the label changes for every item in the loop (note: loop, not array, because of another issue), but the form doesn't have a chance to repaint anything until after the loop is already finished. By this time we're already up to the last item, so that's what we see.

The typical fix for this is move the index outside of the method, and only show one item for each event:

int currentPosition = 0;
public void btnNext_Click(object sender, EventArgs e)
{
    lblScrambled.Text = WordsnQuestions.questions [currentPosition++];
    if (currentPosition >= WordsnQuestions.questions.Length) currentPosition = 0;
}

Separate from this, the original code incremented the counter twice per loop iteration... but again, properly designing the interaction avoids this in the first place.

But let's say you wanted an animation effect. Then you might try something like this to slow things down (note only one increment operator is used):

for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
    lblScrambled.Text = WordsnQuestions.questions [i];
    Thread.Sleep(1000);
}

But this still won't work, because the code still does not yield control back to the form so it can repaint the window.

Now you might be tempted to do something like this:

for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
    lblScrambled.Text = WordsnQuestions.questions [i];
    Application.DoEvents();
    Thread.Sleep(1000);
}

This will seem to (finally!) work, because it does allow an event loop to finally process each of WM_PAINT messages. However, there are still all kinds of problems with it. Again, the correct solution is really to think more carefully about how the interaction should work in the first place.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
0

There are two issues here:

  1. i++ is twice so your skipping items #2, 4, 6... and looping over 1, 3, 5 ... etc
  2. lblScrambled.Text will have the last item and program will not fail if you have odd number of items.

Here is the updated code for you to try:

for (int i = 0; i < WordsnQuestions.questions.Length; i++)
{
    lblScrambled.Text += WordsnQuestions.questions [i];
}
tomcat
  • 1,798
  • 2
  • 11
  • 14
  • 1
    The updated code changes the display to show something different. At that point you might as well do `lblScrambled.Text = string.Join("", WordsnQuestions.questions)` and forget the loop. – Heretic Monkey Sep 30 '22 at 21:23