Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I update the GUI from another thread?

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?

like image 824
CruelIO Avatar asked Mar 19 '09 09:03

CruelIO


People also ask

How to update UI from another thread?

In this case, to update the UI from a background thread, you can create a handler attached to the UI thread, and then post an action as a Runnable : Handler handler = new Handler(Looper. getMainLooper()); handler. post(new Runnable() { @Override public void run() { // update the ui from here } });

How do you handle the UI update from multiple threads?

1) Have each thread set a "DataUpdated" flag when new data is available, and have the UI periodically check for new data. 2) Create each thread with a callback to a UI Update(...) method to be called when new data becomes available.

What is GUI thread?

Most MultiThreaded applications have a MainThread. This thread is the one that is created by the OperatingSystem when the application is started. In a GUI application, it is common that the widgets are initialized in that MainThread.

What is UI thread in Android?

User Interface Thread or UI-Thread in Android is a Thread element responsible for updating the layout elements of the application implicitly or explicitly. This means, to update an element or change its attributes in the application layout ie the front-end of the application, one can make use of the UI-Thread.


2 Answers

The simplest way is an anonymous method passed into Label.Invoke:

// Running on the worker thread string newText = "abc"; form.Label.Invoke((MethodInvoker)delegate {     // Running on the UI thread     form.Label.Text = newText; }); // Back on the worker thread 

Notice that Invoke blocks execution until it completes--this is synchronous code. The question doesn't ask about asynchronous code, but there is lots of content on Stack Overflow about writing asynchronous code when you want to learn about it.

like image 53
Marc Gravell Avatar answered Oct 27 '22 15:10

Marc Gravell


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!

like image 35
Ian Kemp Avatar answered Oct 27 '22 17:10

Ian Kemp