c# - thread - wpf update gui




Mein Formular wird nicht richtig angezeigt, wenn es von einem anderen Thread gestartet wird (4)

Hier ist die Situation: Ich entwickle eine einfache Anwendung mit der folgenden Struktur:

  • FormMain (Startpunkt)
  • Formularbenachrichtigung
  • CompleFunktionen

Recht?

Nun, in FormMain habe ich folgende Funktion:

private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority)
{
    Thread oThread = new Thread(pParameterizedThreadStart);
    oThread.CurrentUICulture = Settings.Instance.Language;
    oThread.IsBackground = true;
    oThread.Priority = pThreadPriority;
    oThread.Name = "μRemote: Background operation";
    oThread.Start(pParameters);
}

Jedes Mal, wenn ich eine zeitaufwändige Methode aufrufen muss, die sich auf ComplexFunctions befindet, mache ich Folgendes:

// This is FormMain.cs
string strSomeParameter = "lala";
DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal);

Die andere Klasse, FormNotification, ist ein Formular, das dem Benutzer Informationen über den Prozess anzeigt. Diese FormNotification kann von FormMain oder ComplexFunctions aufgerufen werden. Beispiel:

// This is ComplexFunctions.cs
public void DoSomething(string pSomeParameter)
{
    // Imagine some time consuming task
    FormNotification formNotif = new FormNotification();
    formNotif.Notify();
}

FormNotify hat einen Timer, schließt also nach 10 Sekunden das Formular. Ich verwende nicht formNotif.ShowDialog, weil ich diesem Formular keinen Fokus geben möchte. Sie können diesen Link überprüfen, um zu sehen, was ich in Notify mache.

Ok, hier ist das Problem: Wenn ich FormNotify von ComplexFunction aufrufe, das von einem anderen Thread in FormMain aufgerufen wird ... verschwindet dieses FormNotify nach ein paar Millisekunden. Es ist der gleiche Effekt, wenn Sie so etwas tun:

using(FormSomething formSomething = new FormSomething)
{
   formSomething.Show();
}

Wie kann das vermieden werden?

Das sind mögliche Lösungen, die ich nicht nutzen möchte:

  • Thread.Sleep (10000) in FormNotify verwenden
  • Verwenden von FormNotif.ShowDialog ()

Dies ist ein vereinfachtes Szenario (FormNotify erledigt einige andere ausgefallene Sachen, die nur 10 Sekunden lang bleiben, aber sie sind irrelevant, um das Problem zu sehen).

Vielen Dank für Ihre Zeit!!! Und bitte, tut mir leid mein Englisch.


Es gibt etwas, das Threadaffinität genannt wird. Es gibt zwei Threads in einer WinForm-Anwendung, eine zum Rendern und eine zum Verwalten der Benutzeroberfläche. Sie befassen sich nur mit Benutzeroberflächenthread. Der Rendering-Thread bleibt verborgen - läuft im Hintergrund. Die einzigen Objekte, die im UI-Thread erstellt werden, können die Benutzeroberfläche manipulieren - dh die Objekte haben eine Thread-Affinität zum UI-Thread.

Seitdem versuchen Sie, die Benutzeroberfläche (eine Benachrichtigung anzeigen) von einem anderen Thread als dem UI-Thread zu aktualisieren. Definieren Sie also in Ihrem Arbeitsthread einen Delegaten und lassen Sie FormMain auf dieses Ereignis warten. Schreiben Sie im Ereignis-Handler (definieren Sie in FormMain) Code, um die FormNotify anzuzeigen.

Schießen Sie das Ereignis aus dem Worker-Thread aus, wenn Sie die Benachrichtigung anzeigen möchten.


Fast jede GUI-Bibliothek ist so ausgelegt, dass nur Aufrufe erlaubt werden, die die GUI ändern, und zwar in einem einzigen Thread, der für diesen Zweck bestimmt ist (der so genannte UI-Thread). Wenn Sie sich in einem anderen Thread befinden, müssen Sie dafür sorgen, dass der Aufruf die GUI im UI-Thread ändert. In .NET ist dies der Aufruf von Invoke (synchron) oder BeginInvoke (asynchron). Der äquivalente Aufruf von Java Swing ist invokeLater () - es gibt ähnliche Funktionen in fast jeder GUI-Bibliothek.

Es gibt etwas, das Threadaffinität genannt wird. Es gibt zwei Threads in einer WinForm-Anwendung, eine zum Rendern und eine zum Verwalten der Benutzeroberfläche. Sie befassen sich nur mit Benutzeroberflächenthread. Der Rendering-Thread bleibt verborgen - läuft im Hintergrund. Die einzigen Objekte, die im UI-Thread erstellt werden, können die Benutzeroberfläche manipulieren - dh die Objekte haben eine Thread-Affinität zum UI-Thread.

Seitdem versuchen Sie, die Benutzeroberfläche (eine Benachrichtigung anzeigen) von einem anderen Thread als dem UI-Thread zu aktualisieren. Definieren Sie also in Ihrem Arbeitsthread einen Delegaten und lassen Sie FormMain auf dieses Ereignis warten. Schreiben Sie im Ereignis-Handler (definieren Sie in FormMain) Code, um die FormNotify anzuzeigen.

Schießen Sie das Ereignis aus dem Worker-Thread aus, wenn Sie die Benachrichtigung anzeigen möchten.

Wenn ein anderer Thread als der Erstellungs-Thread eines Steuerelements versucht, auf eine der Methoden oder Eigenschaften dieses Steuerelements zuzugreifen, führt dies häufig zu unvorhersehbaren Ergebnissen. Eine häufige ungültige Threadaktivität ist ein Aufruf des falschen Threads, der auf die Handle-Eigenschaft des Steuerelements zugreift. Setzen Sie CheckForIllegalCrossThreadCalls auf true, um diese Threadaktivität beim Debuggen einfacher zu finden und zu diagnostizieren. Beachten Sie, dass illegale Cross-Thread-Aufrufe immer eine Ausnahme auslösen, wenn eine Anwendung außerhalb des Debuggers gestartet wird.

Hinweis: Die Einstellung CheckForIllegalCrossThreadCalls sollte nur in DEBUGGIN-SITUATIONEN durchgeführt werden. Unvorhersehbare Ergebnisse werden auftreten und Sie werden versuchen, Bugs zu jagen, dass Sie ein schwieriges Ergebnis haben werden.



Angenommen, Sie haben eine Schaltfläche im Formular und möchten ein anderes Formular Form1 öffnen, wenn der Benutzer auf diese Schaltfläche klickt

    private void button1_Click(object sender, EventArgs e)
    {

        Thread t = new Thread(new ThreadStart(this.ShowForm1));
        t.Start();

    }

Alles, was Sie tun müssen, ist die InvokeRequired-Eigenschaft zu überprüfen und wenn ja, rufen Sie die Invoke-Methode Ihres Formulars auf, die den ShowForm1-Delegaten übergibt, was zu einem rekursiven Aufruf führt, bei dem InvokeRequired false ist.

    delegate void Func();
    private void ShowForm1()
    {            
        if (this.InvokeRequired)
        {
            Func f = new Func(ShowForm1);
            this.Invoke(f);
        }
        else
        {
            Form1 form1 = new Form1();
            form1.Show();
        }            
    }




multithreading