Como usar o WPF Background Worker

177

No meu aplicativo, preciso executar uma série de etapas de inicialização, que levam de 7 a 8 segundos para serem concluídas, durante as quais minha interface do usuário fica sem resposta. Para resolver isso, eu executo a inicialização em um thread separado:

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

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

Eu li alguns artigos sobre oe BackgroundWorkercomo ele deve permitir que eu mantenha meu aplicativo responsivo sem precisar escrever um thread para executar tarefas demoradas, mas não tive sucesso ao tentar implementá-lo. Alguém poderia dizer como eu faria isso usando o BackgroundWorker?

Eamonn McEvoy
fonte
Eu encontrei este tutorial útil, ele tem vários exemplos concisos: elegantcode.com/2009/07/03/...
GrandMasterFlush
Eu recebo um erro de privacidade ao clicar nesse link.
LittleBirdy

Respostas:

319
  1. Adicionar usando
using System.ComponentModel;
  1. Declarar trabalhador em segundo plano :
private readonly BackgroundWorker worker = new BackgroundWorker();
  1. Inscrever-se para eventos:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
  1. Implemente dois 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. Execute assíncrono do trabalhador sempre que necessário.
worker.RunWorkerAsync();
  1. Acompanhar o progresso (opcional, mas geralmente útil)

    a) assinar o ProgressChangedevento e usar ReportProgress(Int32)emDoWork

    b) definido worker.WorkerReportsProgress = true;(créditos para @zagy)

Andrew Orsich
fonte
Existe uma maneira de acessar o DataContext nesses métodos?
susieloo_ 23/07/19
36

Você também pode querer usar os Tasktrabalhadores em segundo plano, em vez dos de segundo plano.

A maneira mais fácil de fazer isso é no seu exemplo Task.Run(InitializationThread);.

Existem vários benefícios em usar tarefas em vez de trabalhadores em segundo plano. Por exemplo, os novos recursos assíncronos / aguardados no .net 4.5 usam Taskpara encadeamento. Aqui está alguma documentação sobre Task https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task

Owen Johnson
fonte
11
Desculpe desenterrar esta discussão, mas o .net 4.0 e 4.5 adicionou algumas coisas legais que são muito mais fáceis de usar do que BackgroundWorker. Na esperança de orientar as pessoas para isso.
Owen Johnson
1
Agora que essa resposta é super antiga, confira asynce await. Essas são formas integradas de linguagem para usar tarefas de uma maneira muito mais legível.
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);  
            }  
        }  
    }  
} 

Além disso, consulte o link abaixo para entender os conceitos de Background:

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

Gul Md Ershad
fonte
3

Achei isso ( WPF Multithreading: Usando o BackgroundWorker e Reporting the Progress to the UI. Link ) para conter o restante dos detalhes que estão faltando na resposta de @ Andrew.

A única coisa que achei muito útil foi que o thread de trabalho não pôde acessar os controles do MainWindow (em seu próprio método); no entanto, ao usar um delegado dentro do manipulador de eventos do Windows principal, era possível.

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
fonte