Background
It's always annoying when I run an application, click a button and the whole application becomes frozen. You don't know whether it has crashed or it is still working. Try to click on any other button, we will get the message "Not responding" or something like that.
That does not sound like a modern application, doesn't it. So don't make your application like that. Whenever your application starts to run a big slow method, make sure you inform your user about that.
Using the Code
BackgroundWorker
is there to help you solve this problem. With this, you don't need to worry about how Asynchronous methods work. To inform users that your application is running, create a Loading Form, run the BackgroundWorker
, show up the Loading Form. Whenever the backgroundworker
is completed, close the form.
Simple right? But I want more. For me, reusable is also important. I need to use Loading Form in different applications, different form. I can't be asked to create a backgroundworker
for every form, every function for which I need it.
So I also create my own Class/Library to make my life easier. This class will auto-create a backgroundworker
, run the code, show the form, and close the form when it finishes. This is one of my favorite classes in my Utilities Belt Library. (I have my utilities class which I use for almost every project that I am working on, can't live without it.)
If you don't know about the basic BackgroundWorker
, you should read the "Basic BackgroundWorker
" part. If you just want to know more about how I create my class, scroll down to the "Make your own class" part.
Basic BackgroundWorker
BackgroundWorker
is a simple (simplest maybe) way to make your app run multi-threading. To using it:
Insert a new BackgroundWorker
into your form.
Insert your function into DoWork
event.
private void bWorker1_DoWork(object sender, DoWorkEventArgs e)
{
decimal ThisMaxValue = Maximum;
if(e.Argument!=null && e.Argument is decimal)
ThisMaxValue = (decimal)e.Argument;
for (int i = 0; i <= ThisMaxValue; i++)
{
Idx = i;
}
}
[Optional] Insert some command into RunWorkerCompleted
if needed.
private void bWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
nudShowResult.Value = Idx;
timer1.Stop();
if (LoadingForm != null && LoadingForm.Visible)
LoadingForm.Dispose();
}
Start running BackgroundWorker
.
private void button4_Click(object sender, EventArgs e)
{
timer1.Start();
bWorker1.RunWorkerAsync();
LoadingForm = new FrmLoading();
LoadingForm.ShowDialog();
}
Make sure to show the form after running the backgroundworker
. If not, the backgroundworker
won't run ask if it needs to wait for the form to close first.
Create a timer if you need to update the value into a control:
private void timer1_Tick(object sender, EventArgs e)
{
nudShowResult.Value = Idx;
}
The function in BackgroundWorker
is not allowed to access any controls because it is in a different thread. If it fails to do so, you will get a really slow error report. So make sure you don't point to any control in the DoWork
event.
To run BackgroundWorker
function with an argument, you could do like this:
bWorker1.RunWorkerAsync(decimal.Parse(textBox1.Text));
Then extract the argument from eventArgs
:
if(e.Argument!=null && e.Argument is decimal)
ThisMaxValue = (decimal)e.Argument;
You could check my code file for more details if necessary.
Make your Own Class
This class isn't exactly what in my Utilities belts class. This is made for the purpose of this tutorial only.
So what we need:
Create a new Library project and add System.Windows.Form
into your project references.
Add those using
into your class file:
using System.Windows.Forms;
using System.ComponentModel;
Add a new form for loading form. It will show up every time the BackgroundWorker
starts and close when it finishes.
Declare a delegate void
. Use this to pass a function into your class:
public BackgroundWorker Bw;
public delegate void RunFunction();
Add some variables for the class:
public BackgroundWorker Bw;
public RunFunction thisFunction;
LoadingForm newLoading;
The construction will be like this:
public BackgroundLoading(RunFunction newFunction)
{
thisFunction = newFunction;
Bw = new BackgroundWorker();
Bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
Bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bw_RunWorkerCompleted);
}
newFunction
is what will run in DoWork
event. Create a new BackgroundWork
and add an event handler for it.
The basic simple code for those event handlers is:
void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
newLoading.Dispose();
MessageBox.Show("Complete");
}
void Bw_DoWork(object sender, DoWorkEventArgs e)
{
if (thisFunction != null)
thisFunction();
}
Add a start
function, which will run the BackgroundWorker
and show up the loading form.
public void Start()
{
Bw.RunWorkerAsync();
newLoading = new LoadingForm();
newLoading.ShowDialog();
}
That's it for the simple loading class.
Using this Class
Add a method, this will run asynchronously.
public void Counter()
{
for (int i = 0; i <= Maximum; i++)
{
Idx = i;
}
}
Create a new class and associate the new method with this class. Then run the class by using its Start
method.
private void button5_Click(object sender, EventArgs e)
{
LoadingClass.BackgroundLoading BL = new LoadingClass.BackgroundLoading(Counter);
BL.Start();
}
That's it! You could try the code examples to see how it really works.
Points of Interest
BackgroundWorker
is a great control which help us to run multi-threads much easier. Using it effectively could make your app much more professional and user friendly.
As I only try to cover a basic approach to BackgroundWorker
, the class was just a simple example. There is a lot of room for improvement. You could add a timer to see how long it runs, or add a GIF animation to make it look cool?
History
- 5th March, 2009: Initial post