"Operation already completed" error when using Progress Bar

user2268507 picture user2268507 · Jan 30, 2015 · Viewed 9.1k times · Source

I currently have the following:

View Model

MovieProcessor movieProcessor = new MovieProcessor(SelectedPath, this);

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += movieProcessor.processMovie_DoWork;
worker.ProgressChanged += worker_ProgressChanged;

progressBar.Show();
worker.RunWorkerAsync();

worker_ProgressChanged

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  CurrentProgress = e.ProgressPercentage;
}

In my MovieProcessor class, I have the method processMovie_DoWork.

public async void processMovie_DoWork(object sender, DoWorkEventArgs e)
{
    for (int x = 0; x < totalFilesToProcess; ++x)
    {
         // Do stuff like calling API

         (sender as BackgroundWorker).ReportProgress(x);

    }
}

The second time ReportProgress(x) is called, I get the error:

This operation has already had OperationCompleted called on it and further calls are illegal.

CurrentProgress is bound to my XAML

<ProgressBar  Minimum="0" Maximum="{Binding MaxValueProgressBar}"  Value="{Binding CurrentProgress, Mode=OneWay}" />

Would anyone have any idea as to what might be happening here?

Answer

Peter Duniho picture Peter Duniho · Jan 30, 2015

To elaborate on the comment from dkozl:

It is possible that the async is causing the problem. There's nothing in the code you posted that would cause an issue, but of course the code example you posted is far from complete.

If you have an await statement in your processMovie_DoWork() method (which is the usual reason one makes a method async), then the method will execute only as far as the point of the first await statement, and then it will exit.

As far as the BackgroundWorker class is considered, this marks the end of the work. It has no way to know that some continuation would be called. Thus, when you call the ReportProgress() method, the operation has in fact completed, making the call to ReportProgress() illegal.

You have a couple of options here:

  1. Get rid of the await statements and perform those operations synchronously. Preferably by calling the synchronous version of the API.
  2. Get rid of the BackgroundWorker and just call your processMovie_DoWork() method directly (albeit likely renamed to something else). In this case, instead of calling the ReportProgress() method, you'd just update the CurrentProgress property directly.

IMHO, the second option is much preferable. You can simply await your processMovie_DoWork() method, and avoid all the hassle of dealing with BackgroundWorker.