2010-06-23 5 views
9
öffnen

In einer WPF-App verwende ich einen BackgroundWorker, um regelmäßig nach einer Bedingung auf dem Server zu suchen. Während das gut funktioniert, möchte ich eine MessageBox öffnen, um die Benutzer zu benachrichtigen, wenn bei der Überprüfung etwas fehlschlägt.Eine MessageBox für die Haupt-App mit Backgroundworker in WPF

Hier ist, was ich habe:

public static void StartWorker() 
{ 
    worker = new BackgroundWorker(); 

    worker.DoWork += DoSomeWork; 
    worker.RunWorkerAsync(); 
} 

private static void DoSomeWork(object sender, DoWorkEventArgs e) 
{ 
    while (!worker.CancellationPending) 
    { 
     Thread.Sleep(5000); 

     var isOkay = CheckCondition(); 

     if(!isOkay) 
      MessageBox.Show("I should block the main window");     
    } 
} 

Aber diese MessageBox blockiert nicht das Hauptfenster. Ich kann immer noch auf meine WPF-App klicken und alles, was ich mag, mit der MessageBox ändern.

Wie löse ich das? Danke,


EDIT:

Als Referenz ist das, was ich am Ende tun:

public static void StartWorker() 
{ 
    worker = new BackgroundWorker(); 

    worker.DoWork += DoSomeWork; 
    worker.ProgressChanged += ShowWarning; 
    worker.RunWorkerAsync(); 
} 

private static void DoSomeWork(object sender, DoWorkEventArgs e) 
{ 
    while (!worker.CancellationPending) 
    { 
     Thread.Sleep(5000); 

     var isOkay = CheckCondition(); 

     if(!isOkay) 
      worker.ReportProgress(1);     
    } 
} 

private static void ShowWarning(object sender, ProgressChangedEventArgs e) 
{ 
    MessageBox.Show("I block the main window"); 
} 

Antwort

6

Anruf ReportProgress und übergeben this zu MessageBox.Show.

10

Es blockiert nicht nur nicht das Hauptfenster, es ist auch sehr wahrscheinlich, dass es dahinter verschwindet. Das ist eine direkte Folge davon, dass es auf einem anderen Thread läuft. Wenn Sie keinen Besitzer für das Meldungsfeld angeben, sucht es nach einem mit der API-Funktion GetActiveWindow(). Es berücksichtigt nur Fenster, die dieselbe Nachrichtenwarteschlange verwenden. Was ist eine threadspezifische Eigenschaft? Eine Message Box zu verlieren, ist natürlich ziemlich schwierig.

In ähnlicher Weise deaktiviert MessageBox nur Fenster, die zu derselben Nachrichtenwarteschlange gehören. Dadurch werden die von Ihrem Haupt-Thread erstellten Fenster nicht blockiert.

Beheben Sie Ihr Problem, indem Sie den UI-Thread das Nachrichtenfeld anzeigen lassen. Verwenden Sie Dispatcher.Invozieren oder nutzen Sie die Ereignisse ReportProgress oder RunWorkerCompleted. Klingt nicht wie die Ereignisse hier angemessen sind.

+0

Vielen Dank, ich hatte das Gefühl, dass dieser Thread ist verwandt, aber ich habe keine Ahnung, wie es zu lösen. Die Verwendung von "ReportProgess" oder "RunWorkerCompleted" war nicht intuitiv, aber das Lesen der Erklärungen macht viel mehr Sinn. –

3

Ersetzen

MessageBox.Show("I should block the main window"); 

mit

this.Invoke((Func<DialogResult>)(() => MessageBox.Show("I should block the main window"))); 

dass das Meldungsfeld auf dem Haupt-Thread verursachen und wird alle Zugriff auf die UI blockieren, bis er eine Antwort erhält. Als zusätzlichen Bonus wird this.Invoke ein Objekt zurückgeben, das in das DialogResult umgewandelt werden kann.

+0

Ich möchte diese Lösung auch versuchen, aber ich kann es nicht kompilieren. Ich rufe dies aus einer normalen Klasse (keine Kontrolle) und es hat nicht auf sie zugreifen. Ich habe auch versucht "Dispatcher.CurrentDispatcher.Invoke()", aber es dauert nicht die Func, die vorgeschlagen wurde –

+0

Ich nahm an, dass dies aus dem Formular aufgerufen wurde. Um diese Methode ausführen zu können, müssen Sie der Klasse einen Verweis auf das übergeordnete Formular übergeben und _ParentForm.Invoke (...) verwenden. Möglicherweise gibt es einen anderen Weg, es zu tun, aber ich weiß nichts von oben mein Kopf. –

+0

Dies kompiliert nicht einmal auf einem Formular –

2

Wie Stephen und Hans gesagt haben, verwenden Sie das ReportProgress-Ereignis und übergeben Sie Daten an den UI-Thread. Dies ist besonders wichtig, wenn Sie etwas anderes als eine MessageBox machen möchten (zum Beispiel ein Steuerelement aktualisieren), weil der Hintergrund-Thread dies nicht direkt tun kann. Sie erhalten eine Cross-Thread-Ausnahme.

Also, was immer Sie tun müssen (Fortschrittsanzeige aktualisieren, Nachrichten auf der Benutzeroberfläche protokollieren usw.), übergeben Sie Daten an den UI-Thread, teilen Sie dem UI-Thread mit, was zu tun ist, aber lassen Sie es vom UI-Thread tun.

1

Ich änderte es so und funktionierte gut für mich

return Application.Current.Dispatcher.Invoke(() => MessageBox.Show(messageBoxText, caption, button, icon)); 
+1

Shoudl ein Kommentar auf die Antwort unter. – user3595247