There are many ways to use BackgroundWorker: lambdas, anonymous delegate and events. All have pros and cons but I find that they are not so readable. Here, you will see an alternative way to use BackgroundWorker.
Introduction
The objective of this tip is to show an alternative way to use BackgroundWorker
.
Background
Microsoft documentation shows how to use BackgroundWorker
with event and in these examples, it's possible to use other methods.
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(
() =>
{
}
);
bgw.DoWork += (sender, e) => { ... }
bgw.DoWork += delegate { ... }
I find these ways not so readable, so I suggest the method in the next chapter.
Using the Code
public class PatientMan
{
private Action _DoWork = null;
private Boolean _IsDoWorkSubscribe = false;
private DoWorkEventHandler _DoWorkHandler;
private Action _RunWorkerCompleted = null;
private Boolean _IsRunWorkerCompletedSubscribe = false;
private RunWorkerCompletedEventHandler _RunWorkerCompletedHandler;
private Action<int> _ProgressChanged = null;
private Boolean _IsProgressChangedSubscribe = false;
private ProgressChangedEventHandler _ProgressChangedHandler;
private BackgroundWorker _Worker = new BackgroundWorker();
public PatientMan()
{
}
public PatientMan(Action doWorkAction,
Action runWorkerCompleted)
{
this.SubcribeDoWork(doWorkAction);
this.SubcribeRunWorkerCompleted(runWorkerCompleted);
}
private void SubcribeDoWork(Action doWorkAction)
{
this._DoWork = doWorkAction;
this._DoWorkHandler = delegate { this._DoWork(); };
this._Worker.DoWork += _DoWorkHandler;
this._IsDoWorkSubscribe = true;
}
private void SubcribeRunWorkerCompleted(Action runWorkerCompleted)
{
this._RunWorkerCompleted = runWorkerCompleted;
this._RunWorkerCompletedHandler
=
delegate {
this._RunWorkerCompleted();
this.UnsubscribeEvents();
};
this._Worker.RunWorkerCompleted += this._RunWorkerCompletedHandler;
this._IsRunWorkerCompletedSubscribe = true;
}
private void SubcribeProgressChanged(Action<int> progressChanged)
{
this._ProgressChanged = progressChanged;
this._ProgressChangedHandler = (obj, ev) =>
{
this._ProgressChanged(ev.ProgressPercentage);
};
this._Worker.ProgressChanged += this._ProgressChangedHandler;
this._Worker.WorkerReportsProgress = true;
this._IsProgressChangedSubscribe = true;
}
public void RunWorkerAsync()
{
this._Worker.RunWorkerAsync();
}
public void ReportProgress(int percentage)
{
if(this._ProgressChanged != null)
{
if (this._IsProgressChangedSubscribe == true)
{
this._Worker.ReportProgress(percentage);
}
else
{
throw new ArgumentNullException();
}
}
else
{
throw new NullReferenceException();
}
}
private void UnsubscribeEvents()
{
if (this._IsDoWorkSubscribe == true)
{
this._Worker.DoWork -= this._DoWorkHandler;
this._IsDoWorkSubscribe = false;
}
if (this._IsRunWorkerCompletedSubscribe == true)
{
this._Worker.RunWorkerCompleted -= this._RunWorkerCompletedHandler;
this._IsRunWorkerCompletedSubscribe = false;
}
if (this._IsProgressChangedSubscribe == true)
{
this._Worker.ProgressChanged -= this._ProgressChangedHandler;
this._Worker.WorkerReportsProgress = false;
this._IsProgressChangedSubscribe = false;
}
}
public void Dispose()
{
this.UnsubscribeEvents();
this._Worker.Dispose();
}
public void SetEvents(Action doWorkAction,
Action runWorkerCompleted,
Action<int> progressChanged,
Boolean unsubscribeEvents = false)
{
this.UnsubscribeEvents();
this.SubcribeDoWork(doWorkAction);
this.SubcribeRunWorkerCompleted(runWorkerCompleted);
this.SubcribeProgressChanged(progressChanged);
}
}
PatientMan
class provides basic features wrapped around BackgroundWorker
.
It's possible to use it in this way:
public class SandBoxPatientMan
{
public SandBoxPatientMan(int algo)
{
switch(algo)
{
case 0:
{
Action doWork = delegate
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
}
};
Action runWorkerCompleted = delegate
{
Console.WriteLine("runWorkerCompleted " + algo);
};
PatientMan patientMan = new PatientMan(doWork, runWorkerCompleted);
patientMan.RunWorkerAsync();
}
break;
case 1:
{
PatientMan patientMan = new PatientMan();
Action doWork = delegate
{
for (int i = 0; i < 10; i++)
{
System.Threading.Thread.Sleep(1000);
patientMan.ReportProgress(i);
}
};
Action runWorkerCompleted = delegate
{
Console.WriteLine("runWorkerCompleted " + algo);
};
Action<int> progressChanged = (percentage) =>
{
Console.WriteLine("progressChanged " + percentage);
};
patientMan.SetEvents(doWork, runWorkerCompleted, progressChanged);
patientMan.RunWorkerAsync();
}
break;
}
}
}
Passing delegates via parameter is more readable and concise.
In my experience, it is also easier to debug.
Points of Interest
This class can't replace complete BackgroundWorker
, it needs some updates like Cancellation
or IsBusy
.
I use it often and I hope the reader finds it useful.
History
In version 2 I updated the code after a suggestion in the comments about memory leaks.
- 14th February, 2020: Version 1
- 17th February, 2020: Version 2