3 Stimmen

Wie übergebe ich GUI-Werte an den Backgroundworker?

Ich arbeite mit einer ziemlich komplexen GUI und versuche, eine Menge Daten von der GUI an einen BackgroudWorker zu übergeben. Das Problem, auf das ich stoße, ist der Zugriff auf einige der GUI-Werte aus dem Background Worker. Zum Beispiel, wenn ich versuche ComboBox.Text Ich bekomme eine InvalidOperationException aufgrund von Überschneidungen. Wenn ich jedoch sage do TextBox.Text scheint alles gut zu funktionieren. Zugegeben, ich bin ziemlich neu in C#, so bin ich ein wenig unklar, warum einige von diesen sind OK und andere nicht.

Ich habe mit mehreren Möglichkeiten kommen, um meine Probleme zu beheben, aber bin auf der Suche nach der besten Praxis von jemandem, der in c# erfahren ist.

Hier sind ein paar Möglichkeiten, die ich mir vorstellen kann, um dies zu beheben

  1. Erstellen Sie eine Klasse/Struktur mit allen Werten, die Sie an den Hintergrund-Worker übergeben möchten, und übergeben Sie diese, wenn Sie RunworkAsync aufrufen. Ich fand dies nicht sehr attraktiv, da ich für jede Seite auf meiner GUI eine Klasse/Struktur erstellen musste, um sie an den BackgroundWorker zu übergeben

  2. Erstellen Sie eine Reihe von verschiedenen Hintergrundarbeitern, die bestimmte Aufgaben haben. Ich hatte immer noch einige Probleme mit der Übergabe von Daten, aber die Menge der Daten, die ich übergeben musste, wurde ziemlich viel reduziert. Allerdings ging die Anzahl der DoWork/ProgressChanged/RunworkerCompleted erheblich, die weniger als ideal war.

  3. (dies führte mich zu dem, was ich derzeit tue)

einen Delegaten und eine Methode zur Erfassung der Informationen erstellen

private delegate string ReadComboDelegate(ComboBox c);

private string ReadComboBox(ComboBox c)
{
    if(c.InvokeRequired)
    {
        ReadComboDelegate del = new ReadComboDelegate(this.ReadComboBox);
        return (string) c.Invoke(del,c);
    }
    else
    {
        return c.Text
    }
}

dann innerhalb DoWork tun Sie etwas wie string txt = this.ReadComboBox(this.comboBox1);

Wenn Sie eine einfache grafische Benutzeroberfläche haben und nicht viele Daten übergeben müssen, ist dies ein ziemlich einfaches Problem. Je mehr Elemente und je komplexer die grafische Benutzeroberfläche wird, desto größer wird dieses Problem jedoch. Wenn jemand irgendwelche Informationen hat, die dies einfacher machen, wäre ich dankbar.

Danke

2voto

davisoa Punkte 5339

Das Cross-Threading-Problem, auf das Sie stoßen, ist auf die Anforderung zurückzuführen, dass nur der UI-Thread UI-Steuerelemente "berühren" darf.

Ich denke, die gängigste Methode zur Übergabe von Daten an einen Hintergrund-Worker ist Ihre Lösung Nr. 1: Erstellen Sie eine einfache Struktur, die alle Daten enthält, die zur Durchführung der Verarbeitung benötigt werden.

Dies ist viel einfacher als die Erstellung von ReadXXX-Methoden für jedes Steuerelement in der Benutzeroberfläche, und es definiert, was der Hintergrundprozess benötigt, um seine Aufgabe zu erfüllen...

1voto

Hans Passant Punkte 894572

Es ist eher ein Zufall, dass TextBox diese Ausnahme nicht verursacht. Ihre Eigenschaft Text wird in einem String zwischengespeichert. Das ist bei ComboBox.Text und den meisten anderen Steuereigenschaften nicht der Fall. Hier wird das native Windows-Steuerelement abgefragt, und an diesem Punkt stellt Windows Forms fest, dass Sie versuchen, ein Steuerelement von einem anderen Thread als dem UI-Thread zu verwenden. Das geht nicht.

Sie müssen sich auf jeden Fall etwas einfallen lassen, um diesen Code umzustrukturieren. Er ist nicht nur illegal, sondern auch unglaublich teuer und grundlegend unsicher, da die Benutzeroberfläche aktualisiert werden könnte, während Ihr Worker läuft. Sammeln Sie die Informationen aus den Steuerelementen, die Sie benötigen, in einer kleinen Hilfsklasse und übergeben Sie diese als Argument an die RunWorkerAsync(object)-Überladung. Und erhalten Sie es zurück in DoWork von e.Argument.

0voto

Brian Gideon Punkte 46450

Die Nummer 3 würde ich auf jeden Fall vermeiden. Trotz des Eifers über die Verwendung von Control.Invoke um Worker- und UI-Threads zu koordinieren, wird es oft überstrapaziert und ist in der Regel eine bestenfalls suboptimale Strategie. Ich ziehe Nr. 1 und Nr. 2 der Nr. 3 bei weitem vor. Hier sind die Gründe, warum ich #3 eher vermeide.

  • Es koppelt die UI- und Worker-Threads eng miteinander.
  • Der Worker-Thread kann bestimmen, wie viel Arbeit der UI-Thread verrichtet.
  • Der Worker-Thread muss auf die Antwort des UI-Threads warten, bevor er fortfahren kann.
  • Das ist eine teure Operation.

Ich weiß, dass es für Sie einen gewissen Mehraufwand bedeutet, wenn Sie #1 oder #2 in Gang setzen, aber das Endergebnis wird auf lange Sicht besser sein.

Als Konsequenz aus meiner Antwort ist die Control.Invoke Methode wird oft überstrapaziert, wenn Daten auch in die entgegengesetzte Richtung übertragen werden müssen (vom Worker-Thread zum UI-Thread, wie im Fall des Sendens von Fortschrittsinformationen an die Benutzeroberfläche). Leider ist dies die Methode, die BackgroundWorker verwendet intern mit seinem ReportProgress Methode. Sie ist in der Regel Es ist besser, den UI-Thread eine gemeinsame Datenstruktur für diese Informationen abfragen zu lassen, und zwar aus denselben Gründen wie oben:

  • Der UI-Thread bestimmt, wann und wie oft die Aktualisierung erfolgen soll.
  • Damit wird die Verantwortung für die Aktualisierung des UI-Threads auf den UI-Thread übertragen, wo sie ohnehin hingehört.
  • Es besteht kein Risiko, dass die UI Message Pump überlaufen wird, wie es bei den vom Worker-Thread initiierten Marshaling-Techniken der Fall wäre.

Allerdings bin ich in diesem Sinne pas die vorschlagen, dass Sie auf die BackgroundWorker vollständig. Behalten Sie einfach einige dieser Punkte im Hinterkopf.

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