2010-06-23 12 views
9

In un'app WPF, sto usando un BackgroundWorker per verificare periodicamente una condizione sul server. Mentre ciò funziona, voglio far apparire un MessageBox che notifica agli utenti se qualcosa non funziona durante il controllo.Popping un MessageBox per l'app principale con Backgroundworker in WPF

Ecco quello che ho:

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");     
    } 
} 

Ma questo MessageBox non blocca la finestra principale. Posso ancora fare clic sulla mia app WPF e modificare tutto ciò che mi piace con il MessageBox in giro.

Come posso risolvere questo? Grazie,


EDIT:

Per riferimento, questo è quello che ho finito per fare:

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"); 
} 

risposta

6

chiamata ReportProgress e passo this a MessageBox.Show.

10

Non solo non blocca la finestra principale, è anche molto probabile che scompaia dietro di esso. Questa è una conseguenza diretta del fatto che è in esecuzione su un thread diverso. Quando non si specifica un proprietario per la finestra di messaggio, ne viene cercata una con la funzione API GetActiveWindow(). Considera solo le finestre che utilizzano la stessa coda di messaggi. Quale è una proprietà specifica del thread. Perdere una casella di messaggio è abbastanza difficile da gestire, naturalmente.

Analogamente, MessageBox disattiva solo le finestre che appartengono alla stessa coda di messaggi. E quindi non bloccherà le finestre create dal tuo thread principale.

Risolve il problema lasciando che il thread dell'interfaccia utente visualizzasse la finestra di messaggio. Utilizza Dispatcher.Invoke o utilizza gli eventi ReportProgress o RunWorkerCompleted. Non sembra che gli eventi siano appropriati qui.

+0

Grazie, ho avuto la sensazione che questo è legato thread, ma non ho idea di come risolverlo. L'utilizzo di "ReportProgess" o "RunWorkerCompleted" non era intuitivo, ma la lettura delle spiegazioni ha molti più sensi. –

3

Sostituire

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

con

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

che farà sì che la finestra di messaggio di essere sul thread principale e blocca tutti gli accessi alla interfaccia utente fino a quando non ottiene una risposta. Come bonus aggiuntivo this.Invoke restituirà un oggetto che può essere inserito nel DialogResult.

+0

Anche io voglio provare questa soluzione, ma non riesco a farla compilare. Sto chiamando questo da una classe normale (non un controllo) e non ha Invoke su di esso. Ho anche provato "Dispatcher.CurrentDispatcher.Invoke()" ma non prende il Func che è stato suggerito –

+0

Ho pensato che questo fosse chiamato dal modulo. per essere in grado di fare questo metodo è necessario passare alla classe un riferimento al form padre e usare _ParentForm.Invoke (...), potrebbe esserci un altro modo per farlo ma non ne conosco nessuno la mia testa. –

+0

Questo non viene nemmeno compilato su un modulo –

2

Come hanno detto Stephen e Hans, utilizzare l'evento ReportProgress e passare i dati al thread dell'interfaccia utente. Questo è particolarmente importante se si vuole fare qualcosa di diverso da un MessageBox (per isntance, aggiornare un controllo) perché il thread in background non può farlo direttamente. Otterrai un'eccezione cross-thread.

Quindi, qualunque cosa sia necessario (aggiornare la barra di avanzamento, registrare i messaggi nell'interfaccia utente, ecc.), Passare i dati al thread dell'interfaccia utente, dire al thread dell'interfaccia utente cosa deve essere fatto, ma lasciare che sia il thread dell'interfaccia utente.

1

ho modificato in questo modo e ha funzionato bene per me

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

Shoudl essere un commento sulla risposta sotto. – user3595247