3

I have an array of labels in my app-

    Label[] labels = new Label[8];      

I want to sequentially change the backcolor of those within a loop-

    private void btnPrepare_Click(object sender, EventArgs e)
    {
        Application.DoEvents();
        for (int i = 0; i < 8; i++)
        {
           labels[i].BackColor = System.Drawing.Color.Red;
           System.Threading.Thread.Sleep(2000);
        }
    }

But all changes are appeared together, rather than sequentially.

Any help?

s.k.paul
  • 7,099
  • 28
  • 93
  • 168
  • 1
    It seems like you want to implement a ``ProgressBar`` - Have a look at https://stackoverflow.com/questions/12126889/how-to-use-winforms-progress-bar – Rand Random Oct 10 '17 at 08:46
  • 1
    `DoEvents()` is rarely a good idea. If you need it to work, then you're doing something wrong. – DonBoitnott Oct 10 '17 at 10:55

2 Answers2

6

Put it like this (quick amendment):

private void btnPrepare_Click(object sender, EventArgs e) { 
  //DONE: foreach - no magic numbers (8)
  foreach (var lbl in labels) {
    lbl.BackColor = System.Drawing.Color.Red;
    lbl.Update(); // <- Update == force label repainting

    System.Threading.Thread.Sleep(2000);
  }
}

Application.DoEvents() is evil: it translates all the events, say, form closing, when you want just painting.

A better approach is to use Task instead of Thread:

// async: we're going to put await in the method
private async void btnPrepare_Click(object sender, EventArgs e) { 
  //DONE: foreach - no magic numbers (8)
  foreach (var lbl in labels) {
    lbl.BackColor = System.Drawing.Color.Red;

    // await: No need in force repainting, messages translating etc.
    await Task.Delay(2000);
  }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

Use Control.Update() method, which will make the control to redraw the invalidated regions within its client area.

There are two ways to repaint a form and its contents:

  1. You can use one of the overloads of the Invalidate() method with the Update() method.

  2. You can call the Refresh() method, which forces the control to redraw itself and all its children. This is equivalent to setting the Invalidate() method to true and using it with Update().

The Invalidate() method governs what gets painted or repainted. The Update() method governs when the painting or repainting occurs. If you use the Invalidate() and Update() methods together rather than calling Refresh(), what gets repainted depends on which overload of Invalidate() you use. The Update() method just forces the control to be painted immediately, but the Invalidate() method governs what gets painted when you call the Update() method.

1)

private void btnPrepare_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 8; i++)
    {
       labels[i].BackColor = System.Drawing.Color.Red;
       labels[i].Update();
       System.Threading.Thread.Sleep(2000);
    }
}

2)

private void btnPrepare_Click(object sender, EventArgs e)
{   
    for (int i = 0; i < 8; i++)
    {
        labels[i].BackColor = System.Drawing.Color.Red;
        labels[i].Refresh();
        System.Threading.Thread.Sleep(2000);
    }
}

If you use Application.DoEvents() method (which is unlikely), it will processes all Windows messages currently in the message queue.

When you run a Windows Form application, it creates the new form, which then waits for events to handle. Each time the form handles an event, it processes all the code associated with that event. All other events wait in the queue. While your code handles the event, your application does not respond. For example, the window does not repaint if another window is dragged on top.

If you call DoEvents() in your code, your application can handle the other events. In your example, add DoEvents() to your code, to make your form repaint when another iteration comes. If you remove DoEvents() from your code, your form will not repaint until the end of the loop.

But, unlike the Refresh() and Update() methods, it will process all events (even unnecessary ones).

As a supplement - I would like to add this answer, which briefly (as possible) shows why is DoEvents() is dangerous for use.

SᴇM
  • 7,024
  • 3
  • 24
  • 41
  • I'm not quite sure you've adequately explained the inherent dangers in using `DoEvents()`, but +1 for being among the few who tried. – DonBoitnott Oct 10 '17 at 10:57
  • thanks, I've edited and added some link about `DoWorker`, which pretty well explains why you shouldn't use it. – SᴇM Oct 10 '17 at 11:09