snippetcsharpCritical
How do I update the GUI from another thread?
Viewed 0 times
howfromupdatetheguithreadanother
Problem
Which is the simplest way to update a
-
I have a
-
While
How could I do that?
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?
Solution
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
Call it like this:
If you're using .NET 3.0 or above, you could rewrite the above method as an extension method of the
UPDATE 05/10/2010:
For .NET 3.0 you should use this code:
which uses LINQ and lambda expressions to allow much cleaner, simpler and safer syntax:
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
Hence I added the runtime checks to ensure that the passed-in property does actually belong to the
If anyone has any further suggestions on how to improve this code for compile-time safety, please comment!
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(
Control @this,
Expression> property,
TResult value);
public static void SetPropertyThreadSafe(
this Control @this,
Expression> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;
if (propertyInfo == null ||
!@this.GetType().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
(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:
// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);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!
Code Snippets
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 });
}
}// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);myLabel.SetPropertyThreadSafe("Text", status);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 ||
!@this.GetType().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 });
}
}// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);Context
Stack Overflow Q#661561, score: 829
Revisions (0)
No revisions yet.