Home > Allgemein, Forms > Threadsichere Aktualisierung von GUIs

Threadsichere Aktualisierung von GUIs

5. September 2009 admin

Standardmässig lässt C# es nicht zu, dass das GUI von einem anderen Thread aktualisiert wird, als von jenem der es erstellt hat. C# wirf eine InvalidOperationException mit dem hinweis, dass versucht wurde das GUI von einem Thread zu aktualisieren, welche nicht der Besitzer ist. Es gibt jedoch möglichkeiten wie man trotzdem von anderen Threads auf das GUI schreiben kann. Dazu dient beispielsweise die Eigenschaft

Control.CheckForIllegalCrossThreadCalls

Diese ist in C# von Beginn weg auf True gestellt. Es gibt nun zwei verschiedene Möglichkeiten wie man von anderen Threads trotzdem die GUI Komponenten aufrufen kann:

  • Man stellt die obengenannte Eigenschaft auf  false, womit keine Prüfung mehr erfolgt ob es ein threadübergreifender Vorgang ist
  • Man benutzt die InvokeRequired-Methode welche prüft ob ein threadübergreifender Vorgang vorliegt und Methode nochmals vom GUI-Thread aufgerufen werden soll.

Da die erste Möglichkeit mit einer Codezeile erledigt wird, möchten wir uns die zweite Methode genauer anschauen. Dabei kommen Delegates zum Einsatz. Wie der Name schon sagt werden dabei Methodenaufrufe delegiert.  Der Methodenaufruf wird dabei an den den GUI-Thread übergeben, welche die Methode von sich aus aufruft. Damit haben wir das Problem des threadübergreifenden Zugriffs elegant gelöst!

Als kleines Beispiel habe ich ein Projekt erstellt welche einerseits mit der BeginInvoke-Methode arbeitet und andererseits keine Prüfung beinhaltet (also eine Exception wirft). Um die Abläufe der Aufrufe am besten zu verfolgen wird empfohlen das Projekt im Debugging-Modus auszuführen. So ist klar ersichtlich welche Aufrufe wann passieren und es ist leichter nachvollziehbar. Der zentrale Code mit den Delegates lautet folgendermassen:

delegate void ChangeLabelSizeCallback(float newSize);
private void ChangeLabelSize(float newSize)
{
    if (lblTarget.InvokeRequired)
    {
        ChangeLabelSizeCallback lsc = new ChangeLabelSizeCallback(ChangeLabelSize);
        lblTarget.Invoke(lsc, new Object[] { newSize });
    }
    else
    {
        lblTarget.Font = new Font(lblTarget.Font.FontFamily, newSize);
        lblTarget.Text = "FontSize: " + newSize.ToString();
    }
}

Anbei steht das gesamte Beispiel noch zum Download bereit:

Download

KategorienAllgemein, Forms Tags:
Kommentare sind geschlossen