Jak używać programu WPF Background Worker

177

W mojej aplikacji muszę wykonać serię czynności inicjalizacyjnych, których ukończenie zajmuje 7-8 sekund, podczas których mój interfejs użytkownika przestaje odpowiadać. Aby rozwiązać ten problem, wykonuję inicjalizację w osobnym wątku:

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

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

Przeczytałem kilka artykułów o tym, BackgroundWorkerjak i jak powinno to pozwolić mi zachować responsywność mojej aplikacji bez konieczności pisania wątku w celu wykonania długich zadań, ale nie odniosłem żadnego sukcesu, próbując go wdrożyć, czy ktoś mógłby powiedzieć, jak bym to zrobił to za pomocą BackgroundWorker?

Eamonn McEvoy
źródło
Ten samouczek okazał się przydatny, ma kilka zwięzłych przykładów: elegantcode.com/2009/07/03/…
GrandMasterFlush
Po kliknięciu tego linku pojawia się błąd prywatności.
LittleBirdy

Odpowiedzi:

319
  1. Dodaj używając
using System.ComponentModel;
  1. Zadeklaruj pracownika w tle :
private readonly BackgroundWorker worker = new BackgroundWorker();
  1. Zapisz się na wydarzenia:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
  1. Zaimplementuj dwie metody:
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. Uruchamiaj pracownika asynchronicznego w dowolnym momencie.
worker.RunWorkerAsync();
  1. Śledź postępy (opcjonalne, ale często przydatne)

    a) zapisać się na ProgressChangedwydarzenie i używać ReportProgress(Int32)wDoWork

    b) zestaw worker.WorkerReportsProgress = true;(kredyty do @zagy)

Andrew Orsich
źródło
Czy istnieje sposób uzyskania dostępu do DataContext w tych metodach?
susieloo_
36

Możesz również przyjrzeć się używaniu Taskzamiast pracowników w tle.

Najłatwiej to zrobić na twoim przykładzie Task.Run(InitializationThread);.

Korzystanie z zadań zamiast pracowników w tle ma kilka zalet. Na przykład nowe funkcje async / await w .net 4.5 służą Taskdo obsługi wątków. Oto dokumentacja dotycząca Task https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task

Owen Johnson
źródło
11
Przepraszam, że odkryłem ten wątek, ale .net 4.0 i 4.5 dodały kilka fajnych rzeczy, które są o wiele łatwiejsze w użyciu niż BackgroundWorker. Mając nadzieję, że uda mi się do tego nakierować ludzi.
Owen Johnson
1
Teraz, gdy ta odpowiedź jest bardzo stara, sprawdź asynci await. Są to zintegrowane językowo sposoby korzystania z zadań w znacznie bardziej czytelny sposób.
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);  
            }  
        }  
    }  
} 

Skorzystaj również z poniższego łącza, aby zrozumieć pojęcia Background:

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

Gul Md Ershad
źródło
3

Okazało się, że to ( WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI link. ) Zawiera pozostałe szczegóły, których brakuje w odpowiedzi @ Andrew.

Jedyną rzeczą, która okazała się bardzo przydatna, było to, że wątek roboczy nie mógł uzyskać dostępu do kontrolek MainWindow (w swojej własnej metodzie), jednak podczas używania delegata wewnątrz procedury obsługi zdarzeń głównego okna było to możliwe.

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
źródło