Frage

Ich habe ein einfaches Benutzersteuerelement für Datenbank-Paging, die einen Controller verwendet die tatsächlichen DAL Anrufe durchzuführen. Ich benutze ein BackgroundWorker das schwere Heben durchzuführen, und auf dem OnWorkCompleted Ereignisse, das ich einige Tasten wieder aktivieren, eine TextBox.Text Eigenschaft ändern und ein Ereignis für das übergeordnete Formular erhöhen.

Form A hält meinen Usercontrol. Wenn ich auf einige Knopf klicken, das Formular B öffnet, auch wenn ich „es“ nichts tun und es nur schließen, und versuchen Sie auf der nächsten Seite aus meiner Datenbank zu bringen, wird der OnWorkCompleted auf dem Arbeitsthread aufgerufen (und nicht mein Haupt-Thread), und wirft eine verkanten Ausnahme.

Im Moment habe ich einen Scheck über InvokeRequired am Handler dort hinzugefügt, ist aber nicht der ganze Sinn der OnWorkCompleted ist am Main Thread aufgerufen werden? Warum sollte es nicht funktionieren wie erwartet?

EDIT:

Ich habe es geschafft, das Problem zu arcgis und BackgroundWorker zu verengen. Ich habe die folgende Lösung wich, einen Befehl zu ArcMap fügt hinzu, die eine einfache Form1 mit zwei Tasten öffnet sich.

Die erste Schaltfläche führt eine BackgroundWorker, die für 500 ms und aktualisiert einen Zähler schläft. Im RunWorkerCompleted Methode überprüft es für InvokeRequired und aktualisiert den Titel zu zeigen whethever das Verfahren originaly wurde innerhalb des Haupt-Thread oder der Worker-Thread ausgeführt wird. Die zweite Schaltfläche öffnet nur Form2, die nichts enthält.

Zunächst wird alle Anrufe zu RunWorkerCompletedare innerhalb des Haupt-Thread gemacht (wie erwartet - das ist der whold Punkt der RunWorkerComplete Methode, zumindest von dem, was ich aus dem MSDN auf BackgroundWorker)

Nach dem Öffnen und Schließen Form2 wird die RunWorkerCompleted immer auf dem Arbeitsthread aufgerufen wird. Ich möchte hinzufügen, dass ich nur diese Lösung des Problems lassen wie es ist (Check für InvokeRequired im RunWorkerCompleted-Methode), aber ich möchte verstehen, warum es gegen meine Erwartungen geschieht. In meinem „echten“ Code würde Ich mag immer wissen, dass die RunWorkerCompleted Methode auf dem Haupt-Thread aufgerufen wird.

konnte ich in meinem form.Show(); das Problem an dem BackgroundTesterBtn Befehl Punkt zu - wenn ich ShowDialog() stattdessen verwenden, bekomme ich kein Problem (RunWorkerCompleted läuft immer auf dem Hauptthread). Ich brauche Show() in meinem ArcMap Projekt zu verwenden, so dass der Benutzer nicht auf die Form gebunden werden.

Ich habe auch versucht, den Fehler auf einem normalen WinForms Projekt zu reproduzieren. Ich habe ein einfaches Projekt, das sich öffnet nur die erste Form ohne ArcMap, aber in diesem Fall konnte ich nicht den Fehler reproduzieren - die RunWorkerCompleted lief auf dem Haupt-Thread, ob ich verwendet Show() oder ShowDialog(), vor und nach Form2 öffnen. Ich habe versucht, eine dritte Form hinzufügen, bevor mein Form1 als Hauptform zu handeln, aber es hat nicht auf das Ergebnis.

Hier ist meine einfache sln (VS2005sp1) - es erfordert

ESRI.ArcGIS.ADF (9.2.4.1420)

ESRI.ArcGIS.ArcMapUI (9.2.3.1380)

ESRI.ArcGIS.SystemUI (9.2.3.1380)

War es hilfreich?

Lösung

Es sieht aus wie ein Fehler:

http://connect.microsoft.com/VisualStudio/feedback /ViewFeedback.aspx?FeedbackID=116930

http://thedatafarm.com/devlifeblog/archive/2005 /12/21/39532.aspx

So schlage ich vor, mit dem kugelsicheren (Pseudo-Code):

if(control.InvokeRequired)
  control.Invoke(Action);
else
  Action()

Andere Tipps

  

Ist das nicht der ganze Sinn des OnWorkCompleted ist am Main Thread aufgerufen werden? Warum sollte es nicht funktionieren wie erwartet?

Nein, es ist nicht.
Sie können einfach nicht jeden beliebigen Punkt auf jedem alten Thread laufen gehen. Themen sind nicht höflich Objekte, die man einfach sagen kann „laufen diese, bitte“.

Ein besseren mentales Modell eines Threads ist ein Güterzug. Sobald es geht, dann ist es auf seine eigene Strecke. Sie können nicht, es ist natürlich ändern oder stoppen. Wenn Sie es beeinflussen wollen, müssen Sie entweder warten, bis es zum nächsten Bahnhof bekommt (zB haben es manuell für einige Ereignisse überprüfen) oder entgleisen es (Thread.Abort und CrossThread Ausnahmen haben viel die gleichen Folgen wie einen Zug entgleisen. Vorsicht ..!).

WinForms-Steuerelemente Art unterstützen dieses Verhalten (Sie haben Control.BeginInvoke mit dem Sie jede Funktion auf dem UI-Thread ausgeführt werden können), aber das funktioniert nur, weil sie einen speziellen Haken in die Fenster UI Meldungsverteilschleife haben und einige spezielle Behandlungsroutinen schreiben. So gehen Sie mit der obigen Analogie, ihren Zuge Kontrollen in am Bahnhof und sucht nach neuen Richtungen in regelmäßigen Abständen, und Sie können diese Einrichtung verwenden, um es Ihre eigenen Richtungen zu senden.

Die BackgroundWorker soll für allgemeine Zwecke zu sein (es ist nicht auf den Windows-GUI gebunden werden kann), so kann sie die Fenster Control.BeginInvoke Funktionen nicht nutzen. Es muss davon ausgehen, dass Ihr Haupt-Thread ist ein unaufhaltsamer ‚Zug‘ seine eigene Sache zu tun, so das fertige Ereignis hat in dem Worker-Thread oder gar nicht.

läuft

Doch wie Sie WinForms verwenden, in Ihrem OnWorkCompleted Handler, können Sie das Fenster bekommen auszuführen andere Rückruf, um die BeginInvoke-Funktionalität ich oben erwähnt. Wie folgt aus:

// Assume we're running in a windows forms button click so we have access to the 
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
    var b = new BackgroundWorker();
    b.DoWork += ... blah blah

    // attach an anonymous function to the completed event.
    // when this function fires in the worker thread, it will ask the form (this)
    // to execute the WorkCompleteCallback on the UI thread.
    // when the form has some spare time, it will run your function, and 
    // you can do all the stuff that you want
    b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
    b.RunWorkerAsync(); // GO!
}

void WorkCompleteCallback()
{
    Button.Enabled = false;
    //other stuff that only works in the UI thread
}

Auch, nicht vergessen:

  

Ihr RunWorkerCompleted Ereignishandler sollte immer die Fehler überprüfen und Cancelled Eigenschaften vor dem Ergebnis Eigenschaft zugreifen. Wenn eine Ausnahme ausgelöst wurde oder ob der Vorgang abgebrochen wurde, stellt sich die Result-Eigenschaft Zugriff auf eine Ausnahme aus.

Die BackgroundWorker überprüft, ob die Delegatinstanz, verweist auf eine Klasse, die die Schnittstelle ISynchronizeInvoke unterstützt. Ihre DAL Schicht wahrscheinlich nicht implementiert diese Schnittstelle. Normalerweise würden Sie die BackgroundWorker auf einem Form verwenden, die diese Schnittstelle nicht unterstützt.

Falls Sie die BackgroundWorker von der DAL Schicht verwenden möchten, und wollen von dort aus die Benutzeroberfläche zu aktualisieren, Sie haben drei Möglichkeiten:

  • Sie würden bleiben die Invoke Methode aufrufen
  • implementieren die Schnittstelle ISynchronizeInvoke auf der DAL-Klasse, und leitet die Anrufe manuell (es ist nur drei Methoden und eine Eigenschaft)
  • bevor die BackgroundWorker (so, auf dem UI-Thread) aufrufen, SynchronizationContext.Current aufzuzurufen und die Inhaltsinstanz in einer Instanzvariablen zu speichern. Die SynchronizationContext werden Sie dann die Send Methode, die wird genau das tun, was Invoke tut.

Der beste Ansatz Probleme mit Verkanten in GUI zu vermeiden, ist auf Verwendung SynchronizationContext .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top