I am using a BackgroundWorker in my winforms app to perform a long running task that occurs in another class (performing database operations). Since all the work is done in another class the cancellation is not as simple. I am using an event in the other class (GenerateStats
) to update the progress as the background operation completes. I want to do a similar thing with cancelling the operation. I can't just call cancellationPending
in the DoWork function because the method would never see it until it finishes and that defeats the purpose. I want the cancellation functionality without having the pass the BackgroundWorker to generateForSubject()
. Is there anyway that this can support cancellation from the generateForSubject()
method in GenerateStats
class. This is the instantiation of the class that the operation performs in:
GenerateStats genStats = new GenerateStats();
This is my DoWork function, which calls the ReportProgress
method anytime the event in the other class gets called. It also calls the method from the other class generateForSubject()
that performs the operations.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
genStats.ProgressChanged += (s, pe) => worker.ReportProgress(pe.ProgressPercentage, pe.UserState);
genStats.generateForSubject();
}
This is the button click event handler that should initiate the cancellation and run CancelAsync()
private void btnStop_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
}
}
This is my separate class that performs the operation, along with creating the ProgressChanged event handler so my class can updated the form with info on progress. It would be awesome if cancellation can perform similarly.
public event ProgressChangedEventHandler ProgressChanged;
protected virtual void OnProgressChanged(int progress, string message)
{
if (ProgressChanged != null)
{
ProgressChanged(this, new ProgressChangedEventArgs(progress, message));
}
}
public void generateForSubject()
{
//Perform db operation not important, but it takes time
OnProgressChanged(33, "Finished 1st set of stats");
//I hope to check for cancellation here
//Perform db operation not important, but it takes time
OnProgressChanged(66, "Finished 2nd set of stats");
//I hope to check for cancellation here
//Perform db operation not important, but it takes time
OnProgressChanged(99, "Finished 3rd set of stats");
//I hope to check for cancellation here
}
Just to clarify if there is any uncertainty as to what I am asking, is there any way for me to support cancellation of my backgroundWorker in the other class without passing the backgroundWorker to the method. If there is absolutely no way and I have to, then I will pass the backgroundWorker
It would be helpful if you could be more specific about your reluctance to pass the
BackgroundWorker
instance. Knowing why that's a design requirement could help produce a better answer to your question.That said, you can apply the same philosophy you did for the
ProgressChanged
event and delegate the cancellation check as well. For example:This allows the
GenerateStats
class to check for pending cancellation without having direct access to theBackgroundWorker
instance, just as theProgressChanged
event allows it to report progress via theBackgroundWorker
without direct access to theBackgroundWorker
.