Categories
.net c# multithreading user-interface winforms

How do I update the GUI from another thread?

1541

Which is the simplest way to update a Label from another Thread?

  • I have a Form running on thread1, and from that I’m starting another thread (thread2).

  • While thread2 is processing some files I would like to update a Label on the Form with the current status of thread2‘s work.

How could I do that?

6

  • 27

    Doesn’t .net 2.0+ have the BackgroundWorker class just for this. It UI thread aware. 1. Create a BackgroundWorker 2. Add two delegates (one for processing, and one for completion)

    Mar 19, 2009 at 10:30

  • 14

    maybe a little late : codeproject.com/KB/cs/Threadsafe_formupdating.aspx

    – MichaelD

    Jan 19, 2010 at 14:15

  • 4

    See the answer for .NET 4.5 and C# 5.0: stackoverflow.com/a/18033198/2042090

    Aug 3, 2013 at 13:21

  • 5

    This question does not apply to Gtk# GUI. For Gtk# see this and this answer.

    – hlovdal

    Apr 1, 2014 at 21:52

  • 3

    Beware: the answers on this question are now a cluttered mess of OT (“here’s what I did for my WPF app”) and historical .NET 2.0 artifacts.

    – Marc L.

    Jul 11, 2018 at 19:48

811

For .NET 2.0, here’s a nice bit of code I wrote that does exactly what you want, and works for any property on a Control:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);

public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }
}

Call it like this:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

If you’re using .NET 3.0 or above, you could rewrite the above method as an extension method of the Control class, which would then simplify the call to:

myLabel.SetPropertyThreadSafe("Text", status);

UPDATE 05/10/2010:

For .NET 3.0 you should use this code:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);

public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      [email protected]().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

which uses LINQ and lambda expressions to allow much cleaner, simpler and safer syntax:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

Not only is the property name now checked at compile time, the property’s type is as well, so it’s impossible to (for example) assign a string value to a boolean property, and hence cause a runtime exception.

Unfortunately this doesn’t stop anyone from doing stupid things such as passing in another Control‘s property and value, so the following will happily compile:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

Hence I added the runtime checks to ensure that the passed-in property does actually belong to the Control that the method’s being called on. Not perfect, but still a lot better than the .NET 2.0 version.

If anyone has any further suggestions on how to improve this code for compile-time safety, please comment!

7

  • 3

    There are cases when this.GetType() evaluates to the same as propertyInfo.ReflectedType (e.g. LinkLabel on WinForms). I don’t have a large C# experience, but I think that the condition for exception should be: if (propertyInfo == null || ([email protected]().IsSubclassOf(propertyInfo.ReflectedType) && @this.GetType() != propertyInfo.ReflectedType) || @this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)

    – Corvin

    Jan 31, 2011 at 13:03


  • 9

    @lan can this SetControlPropertyThreadSafe(myLabel, "Text", status) be called from another module or class or form

    – Smith

    Jun 23, 2011 at 10:58

  • 83

    The solution provided is unnecessarily complex. See Marc Gravell’s solution, or Zaid Masud’s solution, if you value simplicity.

    Mar 28, 2014 at 17:23

  • 8

    This solution does waste ton’s of resources if you update multiple properties as every Invoke costs a lot of resources. I don’t think this is how the feature of Thread Safety was intended anyway. Do Encapsulte your UI update actions and Invoke it ONCE (and not per property)

    – quadroid

    Aug 6, 2014 at 12:09

  • 4

    Why on earth would you use this code over the BackgroundWorker component?

    – Andy

    Oct 8, 2016 at 17:11

811

For .NET 2.0, here’s a nice bit of code I wrote that does exactly what you want, and works for any property on a Control:

private delegate void SetControlPropertyThreadSafeDelegate(
    Control control, 
    string propertyName, 
    object propertyValue);

public static void SetControlPropertyThreadSafe(
    Control control, 
    string propertyName, 
    object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate               
    (SetControlPropertyThreadSafe), 
    new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(
        propertyName, 
        BindingFlags.SetProperty, 
        null, 
        control, 
        new object[] { propertyValue });
  }
}

Call it like this:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

If you’re using .NET 3.0 or above, you could rewrite the above method as an extension method of the Control class, which would then simplify the call to:

myLabel.SetPropertyThreadSafe("Text", status);

UPDATE 05/10/2010:

For .NET 3.0 you should use this code:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
    Control @this, 
    Expression<Func<TResult>> property, 
    TResult value);

public static void SetPropertyThreadSafe<TResult>(
    this Control @this, 
    Expression<Func<TResult>> property, 
    TResult value)
{
  var propertyInfo = (property.Body as MemberExpression).Member 
      as PropertyInfo;

  if (propertyInfo == null ||
      [email protected]().IsSubclassOf(propertyInfo.ReflectedType) ||
      @this.GetType().GetProperty(
          propertyInfo.Name, 
          propertyInfo.PropertyType) == null)
  {
    throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
  }

  if (@this.InvokeRequired)
  {
      @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> 
      (SetPropertyThreadSafe), 
      new object[] { @this, property, value });
  }
  else
  {
      @this.GetType().InvokeMember(
          propertyInfo.Name, 
          BindingFlags.SetProperty, 
          null, 
          @this, 
          new object[] { value });
  }
}

which uses LINQ and lambda expressions to allow much cleaner, simpler and safer syntax:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

Not only is the property name now checked at compile time, the property’s type is as well, so it’s impossible to (for example) assign a string value to a boolean property, and hence cause a runtime exception.

Unfortunately this doesn’t stop anyone from doing stupid things such as passing in another Control‘s property and value, so the following will happily compile:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

Hence I added the runtime checks to ensure that the passed-in property does actually belong to the Control that the method’s being called on. Not perfect, but still a lot better than the .NET 2.0 version.

If anyone has any further suggestions on how to improve this code for compile-time safety, please comment!

7

  • 3

    There are cases when this.GetType() evaluates to the same as propertyInfo.ReflectedType (e.g. LinkLabel on WinForms). I don’t have a large C# experience, but I think that the condition for exception should be: if (propertyInfo == null || ([email protected]().IsSubclassOf(propertyInfo.ReflectedType) && @this.GetType() != propertyInfo.ReflectedType) || @this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)

    – Corvin

    Jan 31, 2011 at 13:03


  • 9

    @lan can this SetControlPropertyThreadSafe(myLabel, "Text", status) be called from another module or class or form

    – Smith

    Jun 23, 2011 at 10:58

  • 83

    The solution provided is unnecessarily complex. See Marc Gravell’s solution, or Zaid Masud’s solution, if you value simplicity.

    Mar 28, 2014 at 17:23

  • 8

    This solution does waste ton’s of resources if you update multiple properties as every Invoke costs a lot of resources. I don’t think this is how the feature of Thread Safety was intended anyway. Do Encapsulte your UI update actions and Invoke it ONCE (and not per property)

    – quadroid

    Aug 6, 2014 at 12:09

  • 4

    Why on earth would you use this code over the BackgroundWorker component?

    – Andy

    Oct 8, 2016 at 17:11

443

Handling long work

Since .NET 4.5 and C# 5.0 you should use Task-based Asynchronous Pattern (TAP) along with asyncawait keywords in all areas (including the GUI):

TAP is the recommended asynchronous design pattern for new development

instead of Asynchronous Programming Model (APM) and Event-based Asynchronous Pattern (EAP) (the latter includes the BackgroundWorker Class).

Then, the recommended solution for new development is:

  1. Asynchronous implementation of an event handler (Yes, that’s all):

    private async void Button_Clicked(object sender, EventArgs e)
    {
        var progress = new Progress<string>(s => label.Text = s);
        await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress),
                                    TaskCreationOptions.LongRunning);
        label.Text = "completed";
    }
    
  2. Implementation of the second thread that notifies the UI thread:

    class SecondThreadConcern
    {
        public static void LongWork(IProgress<string> progress)
        {
            // Perform a long running work...
            for (var i = 0; i < 10; i++)
            {
                Task.Delay(500).Wait();
                progress.Report(i.ToString());
            }
        }
    }
    

Notice the following:

  1. Short and clean code written in sequential manner without callbacks and explicit threads.
  2. Task instead of Thread.
  3. async keyword, that allows to use await which in turn prevent the event handler from reaching the completion state till the task finished and in the meantime doesn’t block the UI thread.
  4. Progress class (see IProgress Interface) that supports Separation of Concerns (SoC) design principle and doesn’t require explicit dispatcher and invoking. It uses the current SynchronizationContext from its creation place (here the UI thread).
  5. TaskCreationOptions.LongRunning that hints to do not queue the task into ThreadPool.

For a more verbose examples see: The Future of C#: Good things come to those who ‘await’ by Joseph Albahari.

See also about UI Threading Model concept.

Handling exceptions

The below snippet is an example of how to handle exceptions and toggle button’s Enabled property to prevent multiple clicks during background execution.

private async void Button_Click(object sender, EventArgs e)
{
    button.Enabled = false;

    try
    {
        var progress = new Progress<string>(s => button.Text = s);
        await Task.Run(() => SecondThreadConcern.FailingWork(progress));
        button.Text = "Completed";
    }
    catch(Exception exception)
    {
        button.Text = "Failed: " + exception.Message;
    }

    button.Enabled = true;
}

class SecondThreadConcern
{
    public static void FailingWork(IProgress<string> progress)
    {
        progress.Report("I will fail in...");
        Task.Delay(500).Wait();

        for (var i = 0; i < 3; i++)
        {
            progress.Report((3 - i).ToString());
            Task.Delay(500).Wait();
        }

        throw new Exception("Oops...");
    }
}

7

  • 2

    If SecondThreadConcern.LongWork() throws an exception, can it be caught by the UI thread? This is an excellent post, btw.

    – kdbanman

    Aug 13, 2015 at 17:53

  • 2

    I have added an additional section to the answer to fulfil your requirements. Regards.

    Aug 14, 2015 at 8:14

  • 3

    The ExceptionDispatchInfo class is responsible for that miracle of rethrowing background exception on UI thread in async-await pattern.

    Aug 18, 2015 at 6:45

  • 1

    Is it just me in thinking that this way of doing this is way more verbose than just invoking Invoke/Begin?!

    – MeTitus

    Sep 28, 2015 at 12:36

  • 4

    Task.Delay(500).Wait()? What’s the point of creating a Task to just block the current thread? You should never block a thread pool thread!

    – Yarik

    Dec 15, 2016 at 19:22