4

I have a Winform program that does some calculations when the user clicks on a button and then calls the picturebox paint event to draw a new BMP based on the results of the calculations. This works fine.

Now I want to do this 100 times and every time the picturebox is refreshed, I want to see the iteration that it's currently in by updating the text on a label as per below:

 private void button2_Click(object sender, EventArgs e)
        {

        for (int iterations = 1; iterations <= 100; iterations++)
        {
            // do some calculations to change the cellmap parameters
            cellMap.Calculate();

            // Refresh picturebox1
            pictureBox1.Invalidate();
            pictureBox1.Update();

            // Update label with the current iteration number
            label1.Text = iterations.ToString();
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {

        Bitmap bmp = new Bitmap(cellMap.Dimensions.Width, cellMap.Dimensions.Height);
        Graphics gBmp = Graphics.FromImage(bmp);

        int rectWidth = scaleFactor;
        int rectHeight = scaleFactor;

         // Create solid brushes
        Brush blueBrush = new SolidBrush(Color.Blue);
        Brush greenBrush = new SolidBrush(Color.Green);
        Brush transparentBrush = new SolidBrush(Color.Transparent);

        Graphics g = e.Graphics;

        for (int i = 0; i < cellMap.Dimensions.Width; i++)
        {
                for (int j = 0; j < cellMap.Dimensions.Height; j++)
                {
                    // retrieve the rectangle and draw it
                    Brush whichBrush;

                    if (cellMap.GetCell(i, j).CurrentState == CellState.State1)
                    {
                        whichBrush = blueBrush;
                    }
                    else if (cellMap.GetCell(i, j).CurrentState == CellState.State2)
                    {
                        whichBrush = greenBrush;
                    }
                    else
                    {
                        whichBrush = transparentBrush;
                    }

                    // draw rectangle to bmp
                    gBmp.FillRectangle(whichBrush, i, j, 1f, 1f);
                }
         }

         g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
         g.DrawImage(bmp, 0, 0, pictureBox1.Width, pictureBox1.Height);
    }

The problem I am having is that the label text only gets displayed after the last picturebox update is completed. So essentially, it does not display 1 through to 99. I can see the picturebox updates after every refresh as the BMP changes with every iteration. Any idea?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
deutschZuid
  • 1,028
  • 2
  • 15
  • 33
  • Did you try to call `label1.Invalidate();` or/and `label1.Update();` after text change? – horgh Feb 08 '13 at 02:11
  • No, but after adding these two statements, it started working. I don't understand why though? – deutschZuid Feb 08 '13 at 02:30
  • Try reading this [Control.Update Method](http://msdn.microsoft.com/en-us/library/system.windows.forms.control.update.aspx) – horgh Feb 08 '13 at 02:33
  • While it's theoretically possible to force updates during a loop that would otherwise block the UI thread, it's better to not block the UI thread in the first place. See duplicate for the non-kludge way to address this issue (as opposed to the definitely-kludge ways seen in the two answers below). – Peter Duniho Jun 01 '21 at 17:34

2 Answers2

10
// Code fragement...
// 5 cent solution, add Invalidate/Update
label1.Text = iterations.ToString();
label1.Invalidate();
label1.Update();
John
  • 633
  • 4
  • 9
  • 2
    I don't know why this answer is getting upvoted. The correct answer is to use `await` and `async`. – Icemanind Jun 14 '18 at 13:11
  • label1.Update() Works for me. I have an event handler and inside the event i changed de text of the label, but Update() is necesaary after change the property Text in my label. Thanks – James K Jun 01 '21 at 17:29
4

To answer your question about why you have to do it: Windows Forms Programs run everything in a single thread - the UI thread. This means it must execute code in order, so that it will finish a function before it can switch back to the UI code. In other words, it can't update the pictures until after its finished with the function, so if you updated the picture 100 times, only the last one will actually get updated. Using the Invalidate/Update code tells the compiler to "pause" execution of the function and forces it to update the UI instead of waiting till the end of the function. Hope that helps!

7200rpm
  • 548
  • 1
  • 4
  • 14
  • 1
    Beware that forcing the UI to update takes a lot of time (relatively). Be sure to profile your code to make sure you don't spend most of your time actualy updating the UI instead of doing some calculation. – Maxter Aug 21 '18 at 15:16