This project is read-only.

Using BackgroundWorker in Windows Applications

Apr 25, 2010 at 1:01 AM
Edited Apr 25, 2010 at 3:47 AM

Introduction 

I have been using this template for quick some time to launch background thread in many Win applications.  It's not perfect; but, it setup most of the critical code.

(The actual code can be found in crudwork.MultiThreading.BackgroundTask class.) 

 For your convenience, there are two templates to choose from.  First one uses anonymous inline blocks.  And the second one uses delegates.  Both should have the same functionality.

 

There are several places where you need to make changes.  It would be nice to reference the line number, but i don't know how to enable it.  So for now, please bear with me.  :-)

1) On the #REGION marked as "Set your CancelButton, ProgressBar, and StatusLabel here..."

a) hookup the main form.  This is usually set to 'this'.

b) OPTIONAL: the cancel button.  If set, this button will allow the user to cancel the process abnormally.  This button will be enabled during the process and disabled afterward.

c) progress bar / status label.  These controls will be updated every 1 second interval to inform the user of the progress

d) OPTIONAL: any controls that needs to be disabled during the process.

2) On the DoWorkEventHander region, add your code to run in the background thread.

3) OPTIONAL: on the initialize region, add any extra initialize code.  The default code should be enough for most task.

4) OPTIONAL: On the ProgressChangedEventHandler region", add any code to update the status and progressbar.  The default code should be enough for most task.

5) OPTIONAL: On the Cleanup region, add any additional code.

For useability purposes, this template has a code section that subscribe to the form's activeForm.FormClosing event to display a warning message to the user, if he/she clicks the form's close button.  (A similiar message is display if the user clicks the Cancel button.)

 

Template 1 - Using Anonymous Inline Block - Delegates or Lambda Expression

 

#region Start a background worker - via anonymous online blocks (using delegates or lamda expressions)
		private void Start1(object argument)
		{
			#region Set your CancelButton, ProgressBar, and StatusLabel here...
			Form activeForm = null;	// set myForm=this; 
			Button cancelButton = new Button();
			ToolStripProgressBar progressBar = new ToolStripProgressBar();
			ToolStripLabel statusLabel = new ToolStripLabel();
			// disable these controls when running...
			Control[] userControls = null;
			#endregion

			bool isRunning = false;
			EventHandler cancel = null;
			FormClosingEventHandler formClosing = null;

			BackgroundTask.Start(
				argument,
				delegate(object sender, DoWorkEventArgs e)
				//(object sender, DoWorkEventArgs e) =>
				{
					#region DoWorkEventHandler - main code goes here...
					// IMPORTANT: Do not access any GUI controls here.  (This code is running in the background thread!)
					var w = (BackgroundWorker)sender;
					//Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

					if (false) // code sample
					{
						int max = (int)e.Argument;
						StatusReport sr = new StatusReport();
						sr.SetBackgroundWorker(w, e, 1);
						sr.ReportProgress("Processing...", 0, max, 0);

						for (int i = 0; i < max; i++)
						{
							sr.Value++;
							sr.ReportProgress();
						}

						sr.ReportProgress("Done");
					}

					#endregion
				},
				delegate(object sender, EventArgs e)
				//(object sender, DoWorkEventArgs e) =>
				{
					#region EventHandler - initialize code goes here...
					// It is safe to access the GUI controls here...
					progressBar.Visible = true;
					progressBar.Minimum = progressBar.Maximum = progressBar.Value = 0;
					isRunning = true;

					#region Hookup event handlers for Button.Canceland Form.FormClosing
					var w = sender as BackgroundWorker;
					if (w != null && w.WorkerSupportsCancellation)
					{
						if (cancelButton != null)
						{
							cancel = (object sender2, EventArgs e2) =>
							{
								//var w = sender2 as BackgroundWorker;
								string msg = "The application is still busy processing.  Do you want to stop it now?";
								if (MessageBox.Show(msg, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
									w.CancelAsync();
							};
							cancelButton.Click += cancel;
							cancelButton.Enabled = true;
						}
					}

					// also, confirm the user if the user clicks the close button.
					if (activeForm != null)
					{
						formClosing = (object sender2, FormClosingEventArgs e2) =>
						{
							if (!isRunning)
								return;

							string msg = "The application is still busy processing.  Do you want to close the application anyway?";
							e2.Cancel = MessageBox.Show(msg, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning,
								MessageBoxDefaultButton.Button2) != DialogResult.Yes;
						};
						activeForm.FormClosing += formClosing;
					}
					#endregion

					FormUtil.Busy(activeForm, userControls, true);
					#endregion
				},
				delegate(object sender, ProgressChangedEventArgs e)
				//(object sender, ProgressChangedEventArgs e) =>
				{
					#region ProgressChangedEventHandler - update progress code goes here...
					// It is safe to access the GUI controls here...
					var sr = e.UserState as StatusReport;
					if (sr == null)
						return;

					StatusReport.UpdateStatusReport(progressBar, sr);
					statusLabel.Text = sr.ToString();
					#endregion
				},
				delegate(object sender, RunWorkerCompletedEventArgs e)
				//(object sender, RunWorkerCompletedEventArgs e) =>
				{
					#region RunWorkerCompletedEventHandler - cleanup code goes here...
					// It is safe to access the GUI controls here...
					try
					{
						if (e.Error != null)
							throw e.Error;
						if (e.Cancelled)
							throw new ThreadInterruptedException("Thread was interrupted by the user.");
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.ToString(), ex.Message);
					}
					finally
					{
						FormUtil.Busy(activeForm, userControls, false);
						progressBar.Visible = false;

						if (cancel != null)
						{
							cancelButton.Click -= cancel;
							cancelButton.Enabled = false;
						}
						if (formClosing != null)
						{
							activeForm.FormClosing -= formClosing;
						}

						isRunning = false;
						statusLabel.Text = "Ready";
					}
					#endregion
				});
		}
		#endregion

 

xx

 Template 2

#region Start a background worker - via anonymous blocks (using delegates using lamda expressions)
		private void Start2(object argument)
		{
			/*
			 * Create anonymous (using delegates using lamda expressions)
			 * */

			#region Set your CancelButton, ProgressBar, and StatusLabel here...
			Form activeForm = null;	// set myForm=this; 
			Button cancelButton = new Button();
			ToolStripProgressBar progressBar = new ToolStripProgressBar();
			ToolStripLabel statusLabel = new ToolStripLabel();
			// disable these controls when running...
			Control[] userControls = null;
			#endregion

			bool isRunning = false;
			EventHandler cancel = null;
			FormClosingEventHandler formClosing = null;

			DoWorkEventHandler doWork =
				delegate(object sender, DoWorkEventArgs e)
				//(object sender, DoWorkEventArgs e) =>
				{
					#region DoWorkEventHandler - main code goes here...
					// IMPORTANT: Do not access any GUI controls here.  (This code is running in the background thread!)
					var w = (BackgroundWorker)sender;
					//Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;

					if (false) // code sample
					{
						int max = (int)e.Argument;
						StatusReport sr = new StatusReport();
						sr.SetBackgroundWorker(w, e, 1);
						sr.ReportProgress("Processing...", 0, max, 0);

						for (int i = 0; i < max; i++)
						{
							sr.Value++;
							sr.ReportProgress();
						}

						sr.ReportProgress("Done");
					}
					#endregion
				};

			EventHandler doInit =
				delegate(object sender, EventArgs e)
				//(object sender, DoWorkEventArgs e) =>
				{
					#region EventHandler - initialize code goes here...
					// It is safe to access the GUI controls here...
					progressBar.Visible = true;
					progressBar.Minimum = progressBar.Maximum = progressBar.Value = 0;
					isRunning = true;

					#region Hookup event handlers for Button.Canceland Form.FormClosing
					var w = sender as BackgroundWorker;
					if (w != null && w.WorkerSupportsCancellation)
					{
						if (cancelButton != null)
						{
							cancel = (object sender2, EventArgs e2) =>
							{
								//var w = sender2 as BackgroundWorker;
								string msg = "The application is still busy processing.  Do you want to stop it now?";
								if (MessageBox.Show(msg, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
									w.CancelAsync();
							};
							cancelButton.Click += cancel;
							cancelButton.Enabled = true;
						}
					}

					// also, confirm the user if the user clicks the close button.
					if (activeForm != null)
					{
						formClosing = (object sender2, FormClosingEventArgs e2) =>
						{
							if (!isRunning)
								return;

							string msg = "The application is still busy processing.  Do you want to close the application anyway?";
							e2.Cancel = MessageBox.Show(msg, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning,
								MessageBoxDefaultButton.Button2) != DialogResult.Yes;
						};
						activeForm.FormClosing += formClosing;
					}
					#endregion

					FormUtil.Busy(activeForm, userControls, true);
					#endregion
				};

			ProgressChangedEventHandler doProgress =
				delegate(object sender, ProgressChangedEventArgs e)
				//(object sender, ProgressChangedEventArgs e) =>
				{
					#region ProgressChangedEventHandler - update progress code goes here...
					// It is safe to access the GUI controls here...
					var sr = e.UserState as StatusReport;
					if (sr == null)
						return;

					StatusReport.UpdateStatusReport(progressBar, sr);
					statusLabel.Text = sr.ToString();
					#endregion
				};

			RunWorkerCompletedEventHandler doComplete =
				delegate(object sender, RunWorkerCompletedEventArgs e)
				//(object sender, RunWorkerCompletedEventArgs e) =>
				{
					#region RunWorkerCompletedEventHandler - cleanup code goes here...
					// It is safe to access the GUI controls here...
					try
					{
						if (e.Error != null)
							throw e.Error;
						if (e.Cancelled)
							throw new ThreadInterruptedException("Thread was interrupted by the user.");
					}
					catch (Exception ex)
					{
						MessageBox.Show(ex.ToString(), ex.Message);
					}
					finally
					{
						FormUtil.Busy(activeForm, userControls, false);
						progressBar.Visible = false;

						if (cancel != null)
						{
							cancelButton.Click -= cancel;
							cancelButton.Enabled = false;
						}
						if (formClosing != null)
						{
							activeForm.FormClosing -= formClosing;
						}

						isRunning = false;
						statusLabel.Text = "Ready";
					}
					#endregion
				};

			BackgroundTask.Start(argument, doWork, doInit, doProgress, doComplete);
		}
		#endregion