This came up in the
forums today[
^] and I figured others may find this code useful... so here it is. A simple background worker implementation (with new progress changed and run worker completed args classes) where all events have access to the initial argument.
public class CustomBackgroundWorker : Component
{
public event DoWorkEventHandler DoWork;
public event EventHandler<CustomProgressChangedEventArgs> ProgressChanged;
public event EventHandler<CustomRunWorkerCompletedEventArgs> RunWorkerCompleted;
private object argument;
private AsyncOperation asyncOperation;
private bool cancellationPending;
private bool isBusy;
private bool workerReportsProgress;
private bool workerSupportsCancellation;
public bool CancellationPending
{
get { return cancellationPending; }
}
public bool IsBusy
{
get { return isBusy; }
}
public bool WorkerReportsProgress
{
get { return workerReportsProgress; }
set { workerReportsProgress = value; }
}
public bool WorkerSupportsCancellation
{
get { return workerSupportsCancellation; }
set { workerSupportsCancellation = value; }
}
private void BeginWork(object obj)
{
object result = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs doWorkEventArgs = new DoWorkEventArgs(argument);
OnDoWork(doWorkEventArgs);
if (doWorkEventArgs.Cancel)
cancelled = true;
else
result = doWorkEventArgs.Result;
}
catch (Exception exception)
{
error = exception;
}
asyncOperation.PostOperationCompleted(
new SendOrPostCallback(EndWork),
new CustomRunWorkerCompletedEventArgs(argument, result, error, cancelled));
}
public void CancelAsync()
{
if (!WorkerSupportsCancellation)
throw new InvalidOperationException("Worker does not support cancellation.");
cancellationPending = true;
}
private void EndWork(object obj)
{
asyncOperation = null;
isBusy = false;
cancellationPending = false;
OnRunWorkerCompleted((CustomRunWorkerCompletedEventArgs)obj);
}
protected virtual void OnDoWork(DoWorkEventArgs e)
{
DoWorkEventHandler eh = DoWork;
if (eh != null)
eh(this, e);
}
protected virtual void OnProgressChanged(CustomProgressChangedEventArgs e)
{
EventHandler<CustomProgressChangedEventArgs> eh = ProgressChanged;
if (eh != null)
eh(this, e);
}
protected virtual void OnRunWorkerCompleted(CustomRunWorkerCompletedEventArgs e)
{
EventHandler<CustomRunWorkerCompletedEventArgs> eh = RunWorkerCompleted;
if (eh != null)
eh(this, e);
}
private void ProgressReporter(object arg)
{
OnProgressChanged((CustomProgressChangedEventArgs)arg);
}
public void ReportProgress(int percentProgress)
{
ReportProgress(percentProgress, null);
}
public void ReportProgress(int percentProgress, object userState)
{
if (!WorkerReportsProgress)
throw new InvalidOperationException("Worker does not report progress.");
CustomProgressChangedEventArgs args = new CustomProgressChangedEventArgs(
argument, percentProgress, userState);
if (asyncOperation != null)
asyncOperation.Post(new SendOrPostCallback(ProgressReporter), args);
else
ProgressReporter(args);
}
public void RunWorkerAsync()
{
RunWorkerAsync(null);
}
public void RunWorkerAsync(object argument)
{
if (isBusy)
throw new InvalidOperationException("Worker is busy.");
this.argument = argument;
isBusy = true;
cancellationPending = false;
asyncOperation = AsyncOperationManager.CreateOperation(null);
new ParameterizedThreadStart(BeginWork).BeginInvoke(argument, null, null);
}
}
public class CustomProgressChangedEventArgs : EventArgs
{
private object argument;
private int progressPercentage;
private object userState;
public CustomProgressChangedEventArgs(object argument, int progressPercentage, object userState)
{
this.argument = argument;
this.progressPercentage = progressPercentage;
this.userState = userState;
}
public object Argument
{
get { return argument; }
}
public int ProgressPercentage
{
get { return progressPercentage; }
}
public object UserState
{
get { return userState; }
}
}
public class CustomRunWorkerCompletedEventArgs : AsyncCompletedEventArgs
{
private object argument;
private object result;
public CustomRunWorkerCompletedEventArgs(object argument, object result, Exception error, bool cancelled) :
base(error, cancelled, null)
{
this.argument = argument;
this.result = result;
}
public object Argument
{
get { return argument; }
}
public object Result
{
get
{
base.RaiseExceptionIfNecessary();
return result;
}
}}