Für .NET 2.0 habe ich ein nettes Stück Code geschrieben, das genau das tut, was Sie wollen, und für jede Eigenschaft einer 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 });
}
}
Sagen wir es so:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Wenn Sie .NET 3.0 oder höher verwenden, können Sie die obige Methode als eine Erweiterungsmethode der Control
Klasse, was dann den Aufruf zu vereinfachen würde:
myLabel.SetPropertyThreadSafe("Text", status);
UPDATE 05/10/2010:
Für .NET 3.0 sollten Sie diesen Code verwenden:
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 });
}
}
die LINQ und Lambda-Ausdrücke verwendet, um eine viel sauberere, einfachere und sicherere Syntax zu ermöglichen:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Jetzt wird nicht nur der Eigenschaftsname bei der Kompilierung geprüft, sondern auch der Typ der Eigenschaft, so dass es unmöglich ist, einer booleschen Eigenschaft einen String-Wert zuzuweisen und damit eine Laufzeitausnahme zu verursachen.
Leider hält das niemanden davon ab, Dummheiten zu begehen, wie z. B. einen anderen zu überholen. Control
Eigenschaft und den Wert, so dass das Folgende problemlos kompiliert werden kann:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Daher habe ich die Laufzeitprüfungen hinzugefügt, um sicherzustellen, dass die übergebene Eigenschaft tatsächlich zu der Control
auf dem die Methode aufgerufen wird. Nicht perfekt, aber immer noch viel besser als die .NET 2.0 Version.
Wenn jemand weitere Vorschläge hat, wie man diesen Code für die Kompilierzeitsicherheit verbessern kann, bitte kommentieren!
28 Stimmen
Hat .net 2.0+ nicht die BackgroundWorker-Klasse genau für diesen Zweck. Es UI Thread bewusst. 1. Erstellen Sie einen BackgroundWorker 2. Fügen Sie zwei Delegierte hinzu (einen für die Verarbeitung und einen für den Abschluss)
15 Stimmen
Vielleicht ein bisschen spät: codeproject.com/KB/cs/Threadsafe_formupdating.aspx
4 Stimmen
Siehe die Antwort für .NET 4.5 und C# 5.0: stackoverflow.com/a/18033198/2042090
5 Stimmen
Diese Frage bezieht sich nicht auf Gtk# GUI. Für Gtk# siehe diese y diese Antwort.
3 Stimmen
Vorsicht: Die Antworten auf diese Frage sind jetzt ein unübersichtliches Durcheinander von OT ("hier ist, was ich für meine WPF-Anwendung getan habe") und historischen .NET 2.0-Artefakten.
0 Stimmen
Ähnlicher Beitrag - GUI-Aktualisierung vom UI-Thread aus erzwingen