Cómo usar WPF Background Worker

177

En mi aplicación, necesito realizar una serie de pasos de inicialización, estos tardan entre 7 y 8 segundos en completarse, durante los cuales mi IU deja de responder. Para resolver esto, realizo la inicialización en un hilo separado:

public void Initialization()
{
    Thread initThread = new Thread(new ThreadStart(InitializationThread));
    initThread.Start();
}

public void InitializationThread()
{
    outputMessage("Initializing...");
    //DO INITIALIZATION
    outputMessage("Initialization Complete");
}

He leído algunos artículos sobre BackgroundWorkery cómo debería permitirme mantener mi aplicación receptiva sin tener que escribir un hilo para realizar tareas largas, pero no he tenido éxito tratando de implementarlo, ¿alguien podría decir cómo lo haría? esto usando el BackgroundWorker?

Eamonn McEvoy
fuente
Este tutorial me pareció útil, tiene varios ejemplos concisos: elegantcode.com/2009/07/03/…
GrandMasterFlush
Recibo un error de privacidad al hacer clic en ese enlace.
LittleBirdy

Respuestas:

319
  1. Agregar usando
using System.ComponentModel;
  1. Declarar trabajador en segundo plano :
private readonly BackgroundWorker worker = new BackgroundWorker();
  1. Suscríbase a eventos:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
  1. Implemente dos métodos:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
  // run all background tasks here
}

private void worker_RunWorkerCompleted(object sender, 
                                           RunWorkerCompletedEventArgs e)
{
  //update ui once worker complete his work
}
  1. Ejecute trabajador asíncrono siempre que lo necesite.
worker.RunWorkerAsync();
  1. Seguimiento del progreso (opcional, pero a menudo útil)

    a) suscribirse al ProgressChangedevento y usar ReportProgress(Int32)enDoWork

    b) conjunto worker.WorkerReportsProgress = true;(créditos a @zagy)

Andrew Orsich
fuente
¿Hay alguna manera de acceder al DataContext en esos métodos?
susieloo_
36

Es posible que desee considerar el uso en Tasklugar de los trabajadores en segundo plano.

La forma más fácil de hacerlo es en su ejemplo es Task.Run(InitializationThread);.

Existen varios beneficios al usar tareas en lugar de trabajadores en segundo plano. Por ejemplo, las nuevas funciones async / await en .net 4.5 se usan Taskpara subprocesos. Aquí hay alguna documentación sobre Task https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task

Owen Johnson
fuente
11
Lamento desenterrar este hilo, pero .net 4.0 y 4.5 agregaron algunas cosas interesantes que son mucho más fáciles de usar que BackgroundWorker. Con la esperanza de dirigir a la gente a eso.
Owen Johnson
1
Ahora que esta respuesta es muy antigua, mira asyncy await. Estas son formas integradas en el lenguaje para usar las tareas de una manera mucho más legible.
Owen Johnson
14
using System;  
using System.ComponentModel;   
using System.Threading;    
namespace BackGroundWorkerExample  
{   
    class Program  
    {  
        private static BackgroundWorker backgroundWorker;  

        static void Main(string[] args)  
        {  
            backgroundWorker = new BackgroundWorker  
            {  
                WorkerReportsProgress = true,  
                WorkerSupportsCancellation = true  
            };  

            backgroundWorker.DoWork += backgroundWorker_DoWork;  
            //For the display of operation progress to UI.    
            backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;  
            //After the completation of operation.    
            backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;  
            backgroundWorker.RunWorkerAsync("Press Enter in the next 5 seconds to Cancel operation:");  

            Console.ReadLine();  

            if (backgroundWorker.IsBusy)  
            { 
                backgroundWorker.CancelAsync();  
                Console.ReadLine();  
            }  
        }  

        static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)  
        {  
            for (int i = 0; i < 200; i++)  
            {  
                if (backgroundWorker.CancellationPending)  
                {  
                    e.Cancel = true;  
                    return;  
                }  

                backgroundWorker.ReportProgress(i);  
                Thread.Sleep(1000);  
                e.Result = 1000;  
            }  
        }  

        static void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)  
        {  
            Console.WriteLine("Completed" + e.ProgressPercentage + "%");  
        }  

        static void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
        {  

            if (e.Cancelled)  
            {  
                Console.WriteLine("Operation Cancelled");  
            }  
            else if (e.Error != null)  
            {  
                Console.WriteLine("Error in Process :" + e.Error);  
            }  
            else  
            {  
                Console.WriteLine("Operation Completed :" + e.Result);  
            }  
        }  
    }  
} 

Además, consulte el siguiente enlace para comprender los conceptos de Background:

http://www.c-sharpcorner.com/UploadFile/1c8574/threads-in-wpf/

Gul Md Ershad
fuente
3

Encontré esto ( WPF Multithreading: Using the BackgroundWorker e Reporting the Progress to the UI. Link ) para contener el resto de los detalles que faltan en la respuesta de @ Andrew.

Lo único que encontré muy útil fue que el hilo de trabajo no podía acceder a los controles de MainWindow (en su propio método), sin embargo, al usar un delegado dentro del controlador de eventos de Windows principal, era posible.

worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
    pd.Close();
    // Get a result from the asynchronous worker
    T t = (t)args.Result
    this.ExampleControl.Text = t.BlaBla;
};
lko
fuente