1592 Stimmen

Wie aktualisiere ich die GUI von einem anderen Thread aus?

Welches ist der einfachste Weg, eine Label von einem anderen Thread ?

  • Tengo un Form weiterlaufend thread1 und davon ausgehend eröffne ich ein weiteres Thema ( thread2 ).

  • Während thread2 einige Dateien verarbeitet, würde ich gerne eine Label sur le Form mit dem aktuellen Status von thread2 Arbeit.

Wie könnte ich das tun?

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

4 Stimmen

Siehe die Antwort für .NET 4.5 und C# 5.0: stackoverflow.com/a/18033198/2042090

23voto

ILoveFortran Punkte 3281
Label lblText; //initialized elsewhere

void AssignLabel(string text)
{
   if (InvokeRequired)
   {
      BeginInvoke((Action<string>)AssignLabel, text);
      return;
   }

   lblText.Text = text;           
}

Beachten Sie, dass BeginInvoke() ist vorzuziehen gegenüber Invoke() weil es weniger wahrscheinlich ist, dass es zu Deadlocks kommt (dies ist jedoch kein Problem, wenn man einem Label nur Text zuweist):

Bei der Verwendung von Invoke() Sie warten auf die Rückkehr der Methode. Nun kann es sein, dass Sie im aufgerufenen Code etwas tun, das auf den Thread warten muss, was vielleicht nicht sofort ersichtlich ist, wenn es in einigen Funktionen vergraben ist, die Sie aufrufen, was wiederum indirekt über Event-Handler geschehen kann. Sie würden also auf den Thread warten, der Thread würde auf Sie warten und Sie wären in einer Sackgasse.

Dies führte dazu, dass sich einige unserer veröffentlichten Softwareprogramme aufhängten. Es war einfach genug, dies zu beheben, indem man die Invoke() con BeginInvoke() . Wenn Sie keine Notwendigkeit für einen synchronen Betrieb haben, was der Fall sein kann, wenn Sie einen Rückgabewert benötigen, verwenden Sie BeginInvoke() .

22voto

Frankg Punkte 221

Für viele Zwecke ist es so einfach:

public delegate void serviceGUIDelegate();
private void updateGUI()
{
  this.Invoke(new serviceGUIDelegate(serviceGUI));
}

"serviceGUI()" ist eine Methode auf GUI-Ebene innerhalb des Formulars (this), die beliebig viele Steuerelemente ändern kann. Rufen Sie "updateGUI()" aus dem anderen Thread auf. Parameter können hinzugefügt werden, um Werte zu übergeben, oder (wahrscheinlich schneller) verwenden Sie Variablen im Klassenbereich mit Sperren, wenn die Möglichkeit eines Konflikts zwischen Threads besteht, die auf sie zugreifen und Instabilität verursachen könnten. Verwenden Sie BeginInvoke anstelle von Invoke, wenn der Nicht-GUI-Thread zeitkritisch ist (beachten Sie die Warnung von Brian Gideon).

22voto

ahmar Punkte 359

Als ich auf das gleiche Problem stieß, suchte ich Hilfe bei Google, aber anstatt mir eine einfache Lösung zu geben, verwirrte es mich noch mehr, indem es mir Beispiele für MethodInvoker und blah blah blah. Also habe ich beschlossen, das Problem auf eigene Faust zu lösen. Hier ist meine Lösung:

Erstellen Sie einen Delegierten wie diesen:

Public delegate void LabelDelegate(string s);

void Updatelabel(string text)
{
   if (label.InvokeRequired)
   {
       LabelDelegate LDEL = new LabelDelegate(Updatelabel);
       label.Invoke(LDEL, text);
   }
   else
       label.Text = text
}

Sie können diese Funktion in einem neuen Thread wie folgt aufrufen

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

Nicht zu verwechseln mit Thread(() => .....) . Ich verwende eine anonyme Funktion oder einen Lambda-Ausdruck, wenn ich an einem Thread arbeite. Um die Anzahl der Codezeilen zu reduzieren, können Sie die ThreadStart(..) Methode, die ich hier nicht erklären soll.

21voto

Rotaerk Punkte 231

Dies ist meine C# 3.0-Variante der Lösung von Ian Kemp:

public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
    var memberExpression = property.Body as MemberExpression;
    if (memberExpression == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
        throw new ArgumentException("The 'property' expression must specify a property on the control.");

    if (control.InvokeRequired)
        control.Invoke(
            (Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
            new object[] { control, property, value }
        );
    else
        propertyInfo.SetValue(control, value, null);
}

Sie nennen es so:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. Sie fügt dem Ergebnis der "as MemberExpression" eine Null-Prüfung hinzu.
  2. Sie verbessert die statische Typsicherheit.

Ansonsten ist das Original eine sehr schöne Lösung.

19voto

Die meisten der anderen Antworten sind ein wenig komplex für mich auf diese Frage (ich bin neu in C #), so schreibe ich meine:

Tengo un WPF Anwendung und haben einen Arbeiter wie unten definiert:

Ausgabe:

BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
    // This is my DoWork function.
    // It is given as an anonymous function, instead of a separate DoWork function

    // I need to update a message to textbox (txtLog) from this thread function

    // Want to write below line, to update UI
    txt.Text = "my message"

    // But it fails with:
    //  'System.InvalidOperationException':
    //  "The calling thread cannot access this object because a different thread owns it"
}

Lösung:

workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
    // The below single line works
    txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}

Ich muss noch herausfinden, was die obige Zeile bedeutet, aber sie funktioniert.

Para WinForms :

Lösung:

txtLog.Invoke((MethodInvoker)delegate
{
    txtLog.Text = "my message";
});

0 Stimmen

Die Frage bezog sich auf Winforms, nicht auf WPF.

0 Stimmen

Danke. WinForms-Lösung oben hinzugefügt.

0 Stimmen

...was nur eine Kopie von wie vielen anderen Antworten auf dieselbe Frage ist, aber okay. Warum nicht Teil der Lösung sein und Ihre Antwort einfach löschen?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X