Thursday 10 January 2019

c# - How do I update the GUI from another thread?




What is the simplest way to update a Label from another thread?



I have a Form 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 can I do that?


Answer



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(
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:



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!


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...