-1

When running the code below, labels in the form are supposed to show the values of vx and vy via labels xVelV and yVelV. Unfortunately, both labels are unresponsive during the while loop. However, as the program exits the loop, the values are then updated.

I've tried the same code by defining the vx & vy as properties with get & set methods (like, the set method setting the value of vx and xVelV.text concurrently), but still no change.

Can anybody figure out what I'm doing wrong?

note: there is a g defined outside the while loop (like Graphics g = panel.CreateGraphics();) which is then used to draw rectangles inside the while block.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace fff {
    class FormMain : Form {    
        // ... some code before
        private Label xVelL = new Label();
        private Label yVelL = new Label();
        // ... some code after

        public FormMain() {
            // ... some code here
            this.Controls.Add(xVelV);
            this.Controls.Add(yVelV);
            // ... some code here
        }

        public void RunG() {            
            // ... some code here
            double x = 400.0, y = 050.0, xn, yn, vx, vy, ax, ay;
            // ... some code here
            bool massOut = false;

            while (!massOut) {
                // ... some code here
                vx += ax;
                vy += ay;
                // ****** bug is here below !!! ******
                this.xVelV.Text = vx.ToString();
                this.yVelV.Text = vy.ToString();
                // ****** bug is here above !!! ******
                xn = x + vx;
                yn = y + vy;
                if (xn < 0 || xn > width || yn < 0 || yn > height) {
                    massOut = true;
                }
                else {
                    // ... some code here
                    x = xn;
                    y = yn;
                    // ... some code here
                }
            }
        }
    }
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
ssd
  • 2,340
  • 5
  • 19
  • 37
  • 1
    Try adding `this.Refresh` after you assign the values to your TextBoxes. – Jonathon Chase Dec 26 '15 at 22:19
  • 2
    The form engine cannot update anything until you end your processing. You are running on the same thread of the form controls so until you end your loops no refresh. You need to use a [BackgroundWorker](https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) component and its ProgressChanged event to update the UI – Steve Dec 26 '15 at 22:20

2 Answers2

3

This is because you are running function RunG from UI thread. Which means that all other code (handling messages from other controls) can't run. You must Invoke executing that method, or you can use code like this:

// this code is in method originally calling RunG();
Task.Run(() => {
    RunG();
});

EDIT: As correctly pointed out in comments, using code above is not enough. You must also change methods that are changing UI elements, such as this.xVelV.Text = someValue;. You must Invoke that method:

this.xVelV.Invoke((Action)delegate() { this.xVelV.Text = someValue; });

By Invoking, executed code is running in context of UI thread of that TextBox. Without Invoke any code running in other thread (different from UI thread which created UI element) will cause Exception.

Pavel Pája Halbich
  • 1,529
  • 2
  • 17
  • 22
3

RunG() is executing in response to an event of some kind. Not sure what, but it must be. That means a message got pulled off the message queue in the program's main message loop, framework stuff happened, an event handler got called, it called RunG(), and the main message loop is blocking until all that stuff unwinds.

After that, the message loop can start handling paint messages for those controls, and the UI thread will be free to do that control-updating work.

The controls can't repaint themselves until the main message loop is free. This can't be done with a single thread.

You need to call RunG() in another thread, and Invoke() from that into the UI thread periodically to update UI. Only the UI thread can touch UI objects, so the Invoke() thing is necessary.

Here's a question with many answers about updating UI from out of the UI thread:

How to update the GUI from another thread in C#?

Community
  • 1
  • 1
  • In response to your first sentence ("RunG() is executing in response to an event of some kind."): yeah, it is called by a `KeyDown` event. So, your point makes sense. I'll give it another try with a new thread. – ssd Dec 26 '15 at 22:29
  • 1
    @merkez3110 Easy guess -- not a lot of ways in a winforms application for code to get executed. I added an update about the Invoke business. – 15ee8f99-57ff-4f92-890c-b56153 Dec 26 '15 at 22:36