Using the BackgroundWorker Component with Composite User Controls

The .Net allows you to develop and use in your applications new user-defined controls, which can considerably simplify our coding. First of all we should define the kind of the control we want to discuss.

In Microsoft documentation it is specified that Windows Forms support three kinds of user-defined controls:

  • Composite
  • Extended
  • Custom

A composite controls combine more than one Windows Forms control encapsulated in one unit (a common container). They inherit from the System.Windows.Forms.UserControl class and often are called user controls.

An extended control often are called derived or inherited control. We use this control when most of the functionality we need is already identical to an existing Windows Forms control. We just create a class that inherits from the class (Windows Forms control) we have chosen, and overrides or adds properties and methods.

A custom control should be created from the beginning by inheriting from the Control class. Here we are going to discuss composite user control (or in short: user control).

Using the BackgroundWorker Component allows to run an operation asynchronously. In this article I will show how you can use the BackgroundWorker Component with user controls. The examples are written using C#.

Let's create a small project, named "BGW_UC" (that stands for "BackgroundWorker Component and User Controls"). The project includes one windows form, named "Form1", and one folder, named "UC" (that stands for "User Controls"). First of all we add to the UC folder some base control, named "UC_0". Then we add two user controls ("UC_1" and "UC_2"), which inherit from the base control UC_0. You can make it by two ways: or by means of the inheritance picker (fig. 1) or simply to change a line of your code (fig. 2):

img1.gif 

Figure 1. 

img2.gif 

Figure 2.

Now our project looks like this (fig.3):

img3.gif 

Figure 3.

We add the BackgroundWorker Component (named "bgw") only to our base control. The UC_1 and UC_2 controls (the derived user controls) gain all the non-private data and behavior (including, of course, the bgw) of the base control (UC_0) in addition to any other data or behaviors they define for themselves.

As we have known, the BackgroundWorker Component has three events: DoWork, ProgressChanged and RunWorkerCompleted. We have to use these events in our controls. In addition to it, on some form, where we use our controls, we wish to catch the moment, when these events occur. With this purpose we, first of all, add to the UC_0 the events (for simplicity we add only two events): 

  1. public event EventHandler BGWDoWork;  
  2. public event EventHandler BGWCompleted;  
Then we change default methods 
  1. private void bgw_DoWork(object sender, DoWorkEventArgs e)  
to 
  1. protected virtual void bgw_DoWork(object sender, DoWorkEventArgs e)  
and so on.

The protected keyword allows to access method within its class and by derived classes, and the virtual keyword allows the methods to be overridden in derived classes. In order to run the RunWorkerAsync method of the BackgroundWorker Component we add the method: 

  1. protected void RunWoker()  
  2. {  
  3.     bgw.RunWorkerAsync();  
  4. }  
The full text (code) of our base control UC_0 will be the following:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Drawing;  
  5. using System.Data;  
  6. using System.Text;  
  7. using System.Windows.Forms;  
  8.   
  9. namespace BGW_UC.UC  
  10. {  
  11.     public partial class UC_0 : UserControl  
  12.     {  
  13.         public UC_0()  
  14.         {  
  15.             InitializeComponent();  
  16.         }  
  17.         #region ForClass  
  18.         public event EventHandler BGWDoWork;  
  19.         public event EventHandler BGWCompleted;  
  20.         #endregion  
  21.         protected virtual void bgw_DoWork(object sender, DoWorkEventArgs e)  
  22.         {  
  23.             if (BGWDoWork != null)  
  24.             {  
  25.                 BGWDoWork(this, e);  
  26.             }  
  27.         }  
  28.         protected virtual void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)  
  29.         {  
  30.         }  
  31.         protected virtual void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
  32.         {  
  33.             if (BGWCompleted != null)  
  34.             {  
  35.                 BGWCompleted(this, e);  
  36.             }  
  37.         }  
  38.         protected void RunWoker()  
  39.         {  
  40.             bgw.RunWorkerAsync();  
  41.         }  
  42.     }  
  43. }  
OK! Everything is ready to add some functionality to the UC_1 and UC_2.

First of all we add the override methods bgw_DoWork, bgw_ProgressChanged and bgw_RunWorkerCompleted (fig. 4). 

img4.gif 

Figure 4.

To start our asynchronous operation we use for the UC_1 the method

  1. public void LoadData()  
  2. {  
  3.     base.RunWoker();  
  4. }  
And for the UC_2 the click_button event (preliminary having added the button1 to the UC_2):
  1. private void button1_Click(object sender, EventArgs e)  
  2. {  
  3.     base.RunWoker();  
  4. }

In order to simulate database transactions (of course, you can connect to real database; this simulation is only for our test purpose) we use GetData.dll. With this purpose we add reference to GetData.dll and add two lines of code to the bgw_DoWork method:

  1. GetData.GetDataHelp getData = new GetData.GetDataHelp();  
  2. DataTable dt = getData.getDataSetCities(1000000).Tables[0];   
The full text (code) of the UC_1 control will be the following:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Text;  
  7. using System.Windows.Forms;  
  8.   
  9. namespace BGW_UC.UC  
  10. {  
  11.     public partial class UC_1 : BGW_UC.UC.UC_0  
  12.     {  
  13.         public UC_1()  
  14.         {  
  15.             InitializeComponent();  
  16.         }  
  17.         protected override void bgw_DoWork(object sender, DoWorkEventArgs e)  
  18.         {  
  19.             base.bgw_DoWork(sender, e);  
  20.             GetData.GetDataHelp getData = new GetData.GetDataHelp();  
  21.             DataTable dt = getData.getDataSetCities(1000000).Tables[0];    
  22.         }  
  23.         protected override void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)  
  24.         {  
  25.             base.bgw_ProgressChanged(sender, e);  
  26.         }  
  27.         protected override void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
  28.         {  
  29.             base.bgw_RunWorkerCompleted(sender, e);  
  30.         }  
  31.         public void LoadData()  
  32.         {  
  33.             base.RunWoker();  
  34.         }  
  35.     }  
  36. }  
The full text (code) of the UC_2 control will be the following:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Drawing;  
  5. using System.Data;  
  6. using System.Text;  
  7. using System.Windows.Forms;  
  8.   
  9. namespace BGW_UC.UC  
  10. {  
  11.     public partial class UC_2 : BGW_UC.UC.UC_0 //UserControl  
  12.     {  
  13.         public UC_2()  
  14.         {  
  15.             InitializeComponent();  
  16.         }  
  17.         protected override void bgw_DoWork(object sender, DoWorkEventArgs e)  
  18.         {  
  19.             base.bgw_DoWork(sender, e);  
  20.             GetData.GetDataHelp getData = new GetData.GetDataHelp();  
  21.             DataTable dt = getData.getDataSetCities(1000000).Tables[0];  
  22.         }  
  23.         protected override void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)  
  24.         {  
  25.             base.bgw_ProgressChanged(sender, e);  
  26.         }  
  27.         protected override void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
  28.         {  
  29.             base.bgw_RunWorkerCompleted(sender, e);  
  30.         }  
  31.         private void button1_Click(object sender, EventArgs e)  
  32.         {  
  33.             base.RunWoker();  
  34.         }  
  35.     }  
  36. }  
OK! Now we add to the Form1 the follow control: UC_1 (named "UC_11"), UC_2 (UC_21), button (button1), statusStrips (statusStrip1 and statusStrip2); to the statusStrip1 we add toolStripStatusLabel1 and to the statusStrip2-toolStripStatusLabel2.

To catch the beginning and finish of the data loading we use the BGWDoWork and BGMCompleted events (fig. 5):

img5.gif 

Figure 5.

The full text (code) of the Form1 will be the following:

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Text;  
  7. using System.Windows.Forms;  
  8.   
  9. namespace BGW_UC  
  10. {  
  11.     public partial class Form1 : Form  
  12.     {  
  13.         public Form1()  
  14.         {  
  15.             InitializeComponent();  
  16.         }  
  17.         private void button1_Click(object sender, EventArgs e)  
  18.         {  
  19.             uC_11.LoadData();  
  20.         }  
  21.         private void uC_11_BGWCompleted(object sender, EventArgs e)  
  22.         {  
  23.             toolStripStatusLabel1.Text = "End...UC_1";  
  24.         }  
  25.         private void uC_11_BGWDoWork(object sender, EventArgs e)  
  26.         {  
  27.             toolStripStatusLabel1.Text = "Load...UC_1";  
  28.         }  
  29.         private void uC_21_BGWCompleted(object sender, EventArgs e)  
  30.         {  
  31.             toolStripStatusLabel2.Text = "End...UC_2";  
  32.         }  
  33.         private void uC_21_BGWDoWork(object sender, EventArgs e)  
  34.         {  
  35.             toolStripStatusLabel2.Text = "Load...UC_2";  
  36.         }  
  37.     }  
  38. }  
As you can see, now we have the possibility to test two independent time-consuming processes: the first one we start with the help of the button of the Form1 (for the UC_1), the second process we start with the help of the button of the control itself (for the UC_2). Run the project, click on the "Load UC_1" button. You can see that the first loading starts. But our user interface does not hang. Click on the "Load UC_2" button. Now two processes are running (fig. 6).

img6.gif 

Figure 6.

In some seconds the first process comes to an end (fig. 7) and then the second (fig. 8).

img7.gif 

Figure 7.

img8.gif

Figure 8.