2010-06-23 9 views
9

En una aplicación WPF, estoy usando un BackgroundWorker para verificar periódicamente una condición en el servidor. Si bien funciona bien, quiero mostrar un MessageBox notificando a los usuarios si algo falla durante el control.Saltando un MessageBox para la aplicación principal con Backgroundworker en WPF

Esto es lo que tengo:

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

Pero este cuadro de mensaje no bloquea la ventana principal. Todavía puedo hacer clic en mi aplicación WPF y cambiar todo lo que quiera con el MessageBox.

¿Cómo soluciono esto? Gracias,


EDIT:

Como referencia, esto es lo que terminé haciendo:

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

Respuesta

6

llamada ReportProgress y paso a thisMessageBox.Show.

10

No solo no bloquea la ventana principal, sino que también es muy probable que desaparezca detrás de ella. Esa es una consecuencia directa de que se ejecuta en un hilo diferente. Cuando no especifica un propietario para el cuadro de mensaje, busca uno con la función API GetActiveWindow(). Solo considera las ventanas que usan la misma cola de mensajes. Que es una propiedad específica de subprocesos. Perder un cuadro de mensaje es bastante difícil de manejar, por supuesto.

De manera similar, MessageBox solo deshabilita las ventanas que pertenecen a la misma cola de mensajes. Y así no bloqueará las ventanas creadas por su hilo principal.

Solucione el problema dejando que el subproceso de interfaz de usuario muestre el cuadro de mensaje. Utilice Dispatcher.Invoke o aproveche los eventos ReportProgress o RunWorkerCompleted. No parece que los eventos sean apropiados aquí.

+0

Gracias, tuve la sensación de que esto está relacionado con el hilo, pero no tengo ni idea de cómo solucionarlo. El uso de "ReportProgess" o "RunWorkerCompleted" no fue intuitivo, pero leer las explicaciones tiene mucho más sentido. –

3

Reemplazar

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

con

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

que hará que el cuadro de mensaje para estar en el hilo principal y bloqueará todo el acceso a la interfaz de usuario hasta que recibe una respuesta. Como una ventaja adicional this.Invoke devolverá un objeto que se puede convertir en DialogResult.

+0

También quiero probar esta solución, pero no logro compilarla. Lo llamo de una clase normal (no un control) y no tiene Invoke en él. También probé "Dispatcher.CurrentDispatcher.Invoke()" pero no toma el Func que se sugirió –

+0

Supuse que se llamaba desde el formulario. para poder hacer este método necesitarás pasarle a la clase una referencia al formulario padre y usar _ParentForm.Invoke (...), puede haber otra manera de hacerlo pero no conozco ninguno fuera de la parte superior de mi cabeza. –

+0

Esto ni siquiera se compila en un formulario –

2

Como han dicho Stephen y Hans, use el evento ReportProgress y pase datos al subproceso de la interfaz de usuario. Esto es especialmente importante si desea hacer algo distinto a un MessageBox (por ejemplo, actualice un control) porque el hilo de fondo no puede hacer esto directamente. Obtendrá una excepción entre hilos.

Así que haga lo que necesite (actualice la barra de progreso, registre mensajes en la interfaz de usuario, etc.), pase datos al hilo de la interfaz de usuario, diga a la interfaz de usuario lo que se debe hacer, pero deje que lo haga.

1

he modificado como esto y funcionó bien para mí

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

Shoudl ser un comentario sobre la respuesta debajo. – user3595247

Cuestiones relacionadas