2009-07-30 8 views
17

Creo que necesito algunas precisiones respecto WPFs Dispatcher.Invoke y Dispatcher.BeginInvoke uso.WPF Dispatcher.BeginInvoke y UI/Antecedentes Temas

Supongamos que tengo un poco de 'trabajo' código de larga duración como tal que se invoca en la prensa de un botón en una sencilla aplicación de WPF:

longWorkTextBox.Text = "Ready For Work!"; 
Action workAction = delegate 
    { 
    Console.WriteLine("Starting Work Action"); 
    int i = int.MaxValue; 
    while (i > 0) 
     i--; 
    Console.WriteLine("Ending Work Action"); 
    longWorkTextBox.Text = "Work Complete"; 
    }; 
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); 

Este código se bloquea mi interfaz de usuario, mientras que el workAction se está realizando. Esto se debe a que las invocaciones de Dispatcher siempre se ejecutan en el hilo de la interfaz de usuario, ¿verdad?

Suponiendo esto, Cuál es la mejor práctica para la configuración de mi despachador para ejecutar el workAction en un hilo separado de mi interfaz de usuario? Sé que puedo añadir un BackgroundWorker a mi workAction para evitar que mi interfaz de usuario de bloqueo como tal:

longWorkTextBox.Text = "Ready For Work!"; 
Action workAction = delegate 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += delegate 
    { 
     Console.WriteLine("Starting Slow Work"); 
     int i = int.MaxValue; 
     while (i > 0) 
     i--; 
     Console.WriteLine("Ending Work Action"); 
    }; 
    worker.RunWorkerCompleted += delegate 
    { 
     longWorkTextBox.Text = "Work Complete"; 
    }; 
    worker.RunWorkerAsync(); 
}; 
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); 

¿Hay alguna forma más elegante de hacer esto, además de utilizar el BackgroundWorker? Siempre he escuchado que el BackgroundWorker es peculiar, por lo que tengo curiosidad por conocer algunas alternativas.

Respuesta

24

Honestamente creo que BackgroundWorker es la solución más elegante para esto. No puedo pensar en una forma más sencilla de hacerlo.

+0

Sip. Si desea hacer algo en un hilo de fondo, pero afecta el hilo de la interfaz de usuario cuando haya terminado, BackgroundWorker es bastante fácil de usar. –

+1

En 2013 hay una manera más simple - async/await: http://stackoverflow.com/questions/12654968/is-async-and-await-exclusively-for-gui-based-asynchronous-programming – Cosmin

+0

Me gusta mucho async, pero no funciona bien con todo. ¡Pero es una opción bastante buena! – Malavos

4

La respuesta de Charlie es lo que estás buscando, realmente.

Sin embargo, si es posible, puede ver si puede parcelar su trabajo para que las unidades de trabajo individuales sean pequeñas y no afecten tanto a la UI. Esto le permitiría simplemente usar el Dispatcher directamente. Hay un buen ejemplo de esto en la página WPF Threading: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

+0

¡Artículo impresionante sobre el tema! –

+0

Sí ... generalmente MSDN es el mínimo, pero ese artículo es muy minucioso. –

+0

para obtener más información acerca de la programación asíncrona en WPF, puede leer: http://www.a2zdotnet.com/View.aspx?Id=93. – frameworkninja

2

Como su nombre indica, se ejecutará en segundo plano por lo que no es necesario crear una instancia con el Dispatcher. Además, si desea que este código se ejecute en un WP7, BeginInvoke no obtiene el parámetro de fondo.

Mi recomendación es crear el BackgroundWorker como:

BackgroundWorker worker = new BackgroundWorker; 

y luego crear los manejadores para los eventos:

worker.WorkerReportsProgress = true; 
worker.WorkerSupportsCancellation = true; 
worker.DoWork +=new DoWorkEventHandler(worker_DoWork); 
worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged); 

Y finalmente te llama:

bkwkPlayingLoop.RunWorkerAsync(); 

Es una gran tentación de usar Dispatcher desde dentro de DoWork pero en su lugar, llamar a worker.ReportProgress() y manejar la interfaz de usuario desde allí. De lo contrario, se enfrentarán algunas incoherencias con la activación de eventos de finalización.

4

Yo también no me gusta BackgroundWorker. Una alternativa sencilla puede ser algo como:

using System; 
using System.Threading; 
using System.Windows; 

namespace Sample 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnSourceInitialized(EventArgs e) 
     { 
      base.OnSourceInitialized(e); 
      longWorkTextBox.Text = "Ready For Work!"; 
     } 

     private void startButton_Click(object sender, RoutedEventArgs e) 
     { 
      new Thread(Work).Start(); 
     } 

     void Work() 
     { 
      longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; })); 
      Console.WriteLine("Starting Work Action"); 
      int i = int.MaxValue; 
      while (i > 0) 
       i--; 
      Console.WriteLine("Ending Work Action"); 
      longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; })); 
     } 
    } 
} 

Fácil, no?

0

tareas son más fáciles de usar que los trabajadores de fondo, hacer más cosas, tienen menos problemas y fueron más o menos creado para que los trabajadores de antecedentes no es necesario utilizar más ...

+0

¿Podría usar algunos ejemplos? – LuckyLikey

+0

El trabajador de segundo plano ha sido efectivamente obsoleto por las tareas ... http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html – MattE

+0

Bien ... gracias por este enlace. Estoy evaluando activamente una buena forma de realizar tareas de fondo en WPF. Supongo que tendré una mirada profunda a esta serie. – LuckyLikey