BackgroundWorker Part 2 – Reporting Class Instance Status (C#)

About long time ago, I already wrote about basic of BackgroundWorker component, BackgroundWorker Part 1.
It contained simple example on how to use BackgroundWorker and also ProgressBar. If you want to look then go to this url

This part 2 will go for more advance implementation example. This means application itself is simple but its coding technique is more advance.

As you may know by now that BackgroundWorker makes procedure run asynchronously. For example I have a procedure to run and the progress change of running procedure is reported.
This is possible because of this procedure run asynchronously by BackgroundWorker.

In order to run a BackgroundWorker, we have to use DoWork event handler function. So we need to write a procedure we want inside that DoWork function.
Most of examples show how to just write a whole codes in DoWork. For example below code:

void backgroundworker1_DoWork(object sender, DoWorkEventArgs e)
	{
		for (int i = 1; i <= numericUpDown1.Value; i++)
		{
			if (backgroundworker1.CancellationPending)
			{
				e.Cancel = true;
			}
			else
			{
				backgroundworker1.ReportProgress(Convert.ToInt32(i*100/numericUpDown1.Value));
				System.Threading.Thread.Sleep(100);
			}
		}
	}

So it shows simple looping and send its report progress but the whole iteration coding is inside DoWork. When more complex requirement is needed then the coding become cumbersome.

We need a way to separate main procedure from DoWork function. So, it will also make loose coupling.
This procedure inside DoWork must have asynchronous capability. To accomplish this, I use Thread object. Thread will make asynchronous process.

In this blog post, I have two examples. One uses Thread directly and other uses webclient class to download a file.

First Example, Display Messages

This first example just displays iterative message to listbox control. Messages will be written by backgroundWorker and also I use Thread object for main procedure.

  1. Create A windows form application
  2. Design it so that it looks like below pic:

  3. Make a class “AsyncClass.cs” which acts as class main procedure.
    class AsyncClass
        {
            Thread t;
            worker wrkr;
            private bool stopped;
    
            public void Iterate(int n)
            {
                stopped = false;
                wrkr  = new worker(n);
    
                t = new Thread(new ThreadStart(wrkr.DoWork));
                t.Start();
                while (!t.IsAlive) ;
                
            }
    
            public void StopIterate()
            {
                
                t.Abort();
                t.Join();
                stopped = true;
    
            }
            public String Msg
            {
                get
                {
                    return wrkr.WorkerMsg;
                }
            }
    
            public bool isStopped
            {
                get
                {
                    return stopped;
                }
            }
        }
    
        class worker
        {
            private int n_worker;
            private String msg;
            public worker(int n)
            {
                n_worker = n;
            }
            public void DoWork()
            {
                for (int i = 1; i <= n_worker ; i++)
                {
                    msg = "Displaying Message " + i;
                    Thread.Sleep(1000);
                }
            }
    
            public String WorkerMsg
            {
                get
                {
                    return msg;
                }
            }
    
    
        }
    
  4. Form source code
    public partial class Form3 : Form
        {
            BackgroundWorker bgw;
            AsyncClass asycl;
            public Form3()
            {
                InitializeComponent();
            }
    
            private void start_Click(object sender, EventArgs e)
            {
                bgw.RunWorkerAsync();
            }
    
            private void Form3_Load(object sender, EventArgs e)
            {
                listBox1.Items.Clear();
    
                bgw = new BackgroundWorker();
                bgw.WorkerReportsProgress = true;
                bgw.WorkerSupportsCancellation = true;
    
                bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
                bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
            }
    
            void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                MessageBox.Show("Done");
    
            }
    
            void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                if (e.UserState != null)
                {
                    listBox1.Items.Add(e.UserState);
                }
                
            }
    
            void bgw_DoWork(object sender, DoWorkEventArgs e)
            {
                asycl = new AsyncClass();
                asycl.Iterate(6);
    
                for (int i = 1; i <= 6; i++)
                {
                    System.Threading.Thread.Sleep(1000);
                   
                    if (!asycl.isStopped)
                    {
                        bgw.ReportProgress(0, asycl.Msg);
                    }
                   
                }
            }
    
            private void stop_Click(object sender, EventArgs e)
            { 
                asycl.StopIterate();
                
            }
        }
    

    This form has two buttons which are ‘Start’ and ‘Stop’. Start does start the backgroundworker DoWork and thus start the thread while stop is to stop them.

    As you can see from above code there are asycl= new AsyncClass(); asycl.Iterate(6); lines inside DoWork and that’s processed asynchronously to display message 6 times.
    So If those lines are asynchronous then the BackgroundWorker’s ReportProgress will work and shows it status properly.

  5. Run it and if no error then it looks like below

Second Example, WebClient File Download

In this second example, I show you how to download a file using BackgroundWorker process. Off course the main procedure to download file is using WebClient class.
Webclient is already has asynchronous capability. So it can be implemented right inside DoWork. However to separate a business logic and presentation let’s not to mix.
We can encapsulate Webclient & download process in another class.

  1. Create A Windows Form application project or just a New Form if you have project
  2. Design a form so it looks like below
  3. Create a Downloader class, FileDownload.cs
    class FileDownload
        {
            private String _url, _localfile;
            private long dwsize, ttlsize;
            private WebClient client;
            private int prgpercentage;
            private bool completed, cancelled;
    
            public FileDownload(String url, String localfile)
            {
                _url = url;
                _localfile = localfile;
                completed = false;
                cancelled = false;
    
                client = new WebClient();
                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
                client.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(client_DownloadFileCompleted);
                
            }
    
            void client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            {
                completed = true;
                client.Dispose();
                
            }
    
            void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
            {
                dwsize = e.BytesReceived;
                ttlsize = e.TotalBytesToReceive;
                prgpercentage = e.ProgressPercentage;
            }
    
            public void Save()
            {
                client.DownloadFileAsync(new Uri(_url), _localfile);
                 
            }
    
            public void Cancel()
            {
                cancelled = true;
                client.CancelAsync();
            }
    
            public long DownloadedSize
            {
                get
                {        
                    return dwsize;
                }
            }
    
            public long TotalSize
            {
                get
                {                
                    return ttlsize; 
                }
            }
    
            public  int ProgressPercentage {
                get
                {
                    return prgpercentage;
                }
            }
    
            public  bool isCompleted
            {
                get
                {
                    return completed;
                }
            }
    
            public  bool isCancelled
            {
                get
                {
                    return cancelled;
                }
            }
        }
    
  4. Form source code
    public partial class Form2 : Form
        {
            BackgroundWorker bgw;
            FileDownload fileDownload;
            
            public Form2()
            {
                InitializeComponent();
            }
    
            private void startDownload_Click(object sender, EventArgs e)
            {
            
                bgw.RunWorkerAsync();            
            }
    
            private void Form2_Load(object sender, EventArgs e)
            {
                
                bgw = new BackgroundWorker();
                bgw.WorkerReportsProgress = true;
                bgw.WorkerSupportsCancellation = true;
    
                bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
                bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
                bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
            }
    
            void bgw_DoWork(object sender, DoWorkEventArgs e)
            {
                File.Delete(localfile.Text);
    
                fileDownload = new FileDownload(url.Text, localfile.Text);
                fileDownload.Save();
    
                
                while (!fileDownload.isCompleted)
                {
                    if (bgw.CancellationPending)
                    {
                        e.Cancel = true;
                    }
                    else
                    {
                        bgw.ReportProgress(fileDownload.ProgressPercentage,"Downloaded " + fileDownload.DownloadedSize + " of " + fileDownload.TotalSize);
                        System.Threading.Thread.Sleep(750);
                    }
                }
            }
    
            void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (fileDownload.isCancelled)
                {
                    MessageBox.Show("Cancelled");
                }
                else
                {
                    MessageBox.Show("Completed");
                }
            }
    
            void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                progressBar1.Value = e.ProgressPercentage;
                lblByteMsg.Text = e.UserState.ToString();
            }
    
            private void cancel_Click(object sender, EventArgs e)
            {            
                fileDownload.Cancel();
                
            }
        }
    

    Again in this form, it has two buttons ‘Start’ and ‘Cancel’. Start to download a file via BackgroundWorker process and thus starting Webclient in a background.
    ‘Cancel’ is to cancel in a middle of download process.

    As you can see from above code there are fileDownload = new FileDownload(url.Text, localfile.Text); fileDownload.Save(); lines inside DoWork and that’s processed asynchronously to download a file.
    So since main function is asynchronous then the BackgroundWorker’s ReportProgress will work and shows downloaded bytes progress properly.

  5. Run it and if no error it looks like below

Regards,

Agung Gugiaji

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s