Multithreading en WPF: BackgroundWorker y como comunicarse con la Interfaz de Usuario

Muchas veces en nuestras aplicaciones necesitamos realizar tareas largas que deben ser ejecutados en un thread separado, pero al mismo tiempo mostrar al usuario una barra de progreso o cualquier tipo de notificación para que el usuario sienta que la aplicación no ha dejado de funcionar sino que esta ejecutando una tarea larga y que además puede abortar esa tarea.

Para ello utilizaremos la clase BackgroundWorker, La clase BackgroundWorker permite ejecutar una operación en un subproceso dedicado e independiente, pudiendo el usuario interactuar con la interfaz de usuario mientras se ejecuta la tarea.

Para lanzar una tarea en otro thread utilizaremos el evento DoWork de la clase BackgroundWorker, el código en el event handler del evento DoWork se jecutara en un thread diferente

int maxRecords = 1000;
 
BackgroundWorker worker = new BackgroundWorker();
 
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    for (int x = 1; x < maxRecords; x++)
    {
        System.Threading.Thread.Sleep(10);
    }
};
 
worker.RunWorkerAsync();

 

De esta manera lanzaremos en otro Thread totalmente diferentes el recorrido de los registros

Si queremos pasar parámetros a la tarea , nosotros podemos pasar esos parámetros al método RunWorkerAsync a través de la propiedad Argument del evento DoWork

 

BackgroundWorker worker = new BackgroundWorker();
 
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    string path = (string)args.Argument;
    //hacer algo
};
 
worker.RunWorkerAsync("c:\myFile.txt");

Si queremos retornar un valor como el resultado de un calculo, debemos retornar ese valor a través de la propiedad Result de DoWorkEventArgs en el event handler de DoWork. Este valor será recogido por nosotros en el evento RunWorkerCompleted al que tenemos que suscribirnos

 

BackgroundWorker worker = new BackgroundWorker();
 
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    args.Result = CalculationMethod();
};
 
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
    object result = args.Result;
};
 
worker.RunWorkerAsync();

 

Si queremos cancelar el proceso mientras se esta ejecutando debemos de poner la propiedad WorkerSupportsCancellation a True, de esta manera podemos llamar al metodo CancelAsync para que cancele el proceso. Este método pone la propiedad CancellationPending a true y en ele evnt handler DoWork deberemos comprobarla para abortar el proceso poniendo la propiedad Cancel a true

 

    for (int x = 1; x < maxRecords; x++)
    {
        //check if there is a cancelat
        if (worker.CancellationPending)
        {
            args.Cancel = true;
            return;
        }
 
        System.Threading.Thread.Sleep(10);
    }
};
 
worker.RunWorkerAsync();

 

Si queremos notificar el progreso al usuario a través de la interfaz de usuario, debemos poner primero la propiedad WorkerReportsProgess a true, suscribirnos al evento ProgressChanged que se lanzara por cada cambio de progreso que nosotros notifiquemos en el event handler DoWork a través del método ReportProgress

int maxRecords = 1000;
 
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
 
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    for (int x = 1; x < maxRecords; x++)
    {
        System.Threading.Thread.Sleep(10);
        worker.ReportProgress(Convert.ToInt32(((decimal)x / (decimal)maxRecords) * 100));
    }
};
 
worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
{
    int percentage = args.ProgressPercentage;
};
 
worker.RunWorkerAsync();

 

Si queremos notificar este porcentaje al usuario necesitamos utilizar la clase Dispatcher que nos permite comunicarnos con otro Thread , si queremos coger la referencia de un objeto de la interfaz de usuario como un botón para habilitarlo o deshabilitarlo lo tenemos que referenciar

System.Windows.Threading.Dispatcher aDisp = Button1.Dispatcher;

 

Aquí os dejo un ejemplo de como utilizarlo todo junto para que podáis jugar con ello

 

3 comentarios sobre “Multithreading en WPF: BackgroundWorker y como comunicarse con la Interfaz de Usuario”

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *