Force GUI update from UI Thread

Viewed 138077

In WinForms, how do I force an immediate UI update from UI thread?

What I'm doing is roughly:

label.Text = "Please Wait..."
try 
{
    SomewhatLongRunningOperation(); 
}
catch(Exception e)
{
    label.Text = "Error: " + e.Message;
    return;
}
label.Text = "Success!";

Label text does not get set to "Please Wait..." before the operation.

I solved this using another thread for the operation, but it gets hairy and I'd like to simplify the code.

13 Answers

If you only need to update a couple controls, .update() is sufficient.

btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly

Think I have the answer, distilled from the above and a little experimentation.

progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;

I tried decrementing the value and the screen updated even in debug mode, but that would not work for setting progressBar.Value to progressBar.Maximum, because you cannot set the progress bar value above the maximum, so I first set the progressBar.Value to progressBar.Maximum -1, then set progressBar.Maxiumum to equal progressBar.Value. They say there is more than one way of killing a cat. Sometimes I'd like to kill Bill Gates or whoever it is now :o).

With this result, I did not even appear to need to Invalidate(), Refresh(), Update(), or do anything to the progress bar or its Panel container or the parent Form.

myControlName.Refresh() is a simple solution to update a control before moving on to a "SomewhatLongRunningOperation". From: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.update?view=windowsdesktop-6.0 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.

When I want to update the UI in "real-time" (or based on updates to data or long running operations) I use a helper function to "simplify" the code a bit (here it may seem complex, but it scales upward very nicely). Below is an example of code I use to update my UI:

    // a utility class that provides helper functions
    // related to Windows Forms and related elements
    public static class FormsHelperFunctions {

        // This method takes a control and an action
        // The action can simply be a method name, some form of delegate, or it could be a lambda function (see: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions)
        public static void InvokeIfNeeded(this Control control, Action action)
        {

            // control.InvokeRequired checks to see if the current thread is the UI thread,
            // if the current thread is not the UI thread it returns True - as in Invoke IS required
            if(control.InvokeRequired)
            {
                // we then ask the control to Invoke the action in the UI thread
                control.Invoke(action);
            }
            // Otherwise, we don't need to Invoke
            else
            {
                // so we can just call the action by adding the parenthesis and semicolon, just like how a method would be called.
                action();
            }
        }

    }

    // An example user control
    public class ExampleUserControl : UserControl {

        /*
            //
            //*****
            // declarations of label and other class variables, etc.
            //*****
            //




            ...
        */

        // This method updates a label, 
        // executes a long-running operation, 
        // and finally updates the label with the resulting message.
        public void ExampleUpdateLabel() {

            // Update our label with the initial text
            UpdateLabelText("Please Wait...");

            // result will be what the label gets set to at the end of this method
            // we set it to Success here to initialize it, knowing that we will only need to change it if an exception actually occurs.
            string result = "Success";

            try {
                // run the long operation
                SomewhatLongRunningOperation(); 
            }
            catch(Exception e)
            {
                // if an exception was caught, we want to update result accordingly
                result = "Error: " + e.Message;
            }

            // Update our label with the result text
            UpdateLabelText(result);
        }

        // This method takes a string and sets our label's text to that value
        // (This could also be turned into a method that updates multiple labels based on variables, rather than one input string affecting one label)
        private void UpdateLabelText(string value) {

            // call our helper funtion on the current control
            // here we use a lambda function (an anonymous method) to create an Action to pass into our method
            // * The lambda function is like a Method that has no name, here our's just updates the label, but it could do anything else we needed
            this.InvokeIfNeeded(() => {

                // set the text of our label to the value
                // (this is where we could set multiple other UI elements (labels, button text, etc) at the same time if we wanted to)
                label.Text = value;
            });
        }

    }
Related