C# BackGroundWorker with ProgressBar Updates after

2019-05-20 04:41发布

问题:

I have the following in a button click event:

private void buttonSubmitAchChecks_Click(object sender, EventArgs e)
{
  if (backgroundWorker1.IsBusy) return;
  SubmittingAch(true);

  backgroundWorker1.WorkerReportsProgress = true;
  backgroundWorker1.WorkerSupportsCancellation = true;

  label_Status.Text = "Submitting checks to ACH ....";
  var qry = from ds in checkTrans.IndividualCheck
            where ds.SubmitToACH &&
                  ds.Status == "Entered" &&
                  ds.CheckAmount > 0 &&
                  ds.SubmitToACH
            select ds;

  if (qry.Count() <= 0)
  {
    label_Status.Text = "Nothing to submit. Check the Check Amount, ACH, and Status fields.";
  }
  else
  {
    progressBar1.Maximum = qry.Count();
    progressBar1.Minimum = 0;
    progressBar1.Step = 1;
    backgroundWorker1.RunWorkerAsync(qry);
  }

}

My backgroundWorker1_DoWork:

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
  if (backgroundWorker1.CancellationPending)
  {
    e.Cancel = true;
  }
  else
  {
    var qry = e.Argument as EnumerableRowCollection<CheckTrans.IndividualCheckRow>;
    if (qry != null)
    {
      Thread.Sleep(4000);

      //item.Status = ach.SubmitCheck(item);
      var ach = new SubmitAchChecks();
      foreach (var item in qry)
      {
        ach.SubmitCheck(item);
        backgroundWorker1.ReportProgress(1);
        Console.Write("backgroundWorker1_dowork=" + progressBar1.Value.ToString() + "\r\n");
      }
    }

  }

}

My Cancel Button:

private void cancelAsyncButton_Click(object sender, EventArgs e)
{
  if (backgroundWorker1.WorkerSupportsCancellation == true)
  {
    label_Status.Text = "Cancelling...";
    backgroundWorker1.CancelAsync();
  }
}

My backgroundWorker1_RunWorkerCompleted:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  if (e.Cancelled == true)
  {
    label_Status.Text = "Canceled!";
  }
  else if (e.Error != null)
  {
    label_Status.Text = "Error: " + e.Error.Message;
  }
  else
  {
    label_Status.Text = "Done!";
  }
  SubmittingAch(false);
}

My backgroundWorker1_ProgressChanged:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  progressBar1.Value += 1;
  Console.Write("progressbar1.value=" + progressBar1.Value.ToString() + "\r\n");
}

I get following output in my debug window when I processed 2 items:

backgroundWorker1_dowork=0

backgroundWorker1_dowork=0

progressbar1.value=1

progressbar1.value=2

The event is firing, but as you can see from the console.write, it's happening AFTER the thread finishes. I get the progressbar scrolling, but only once the dowork has completed.

What have I done wrong on this? I'd like it to update as each item is completed.

回答1:

It's due to the way threads work. ProgressChange is invoked on the UI thread using BeginInvoke, and therefore on another thread. Meanwhile, the worker thread continues running. Since there is not much work to do, the BackgroundWorker finishes its work before BeginInvoke actually invokes the method, because thread switches don't happen every CPU operation. They happen after quite a few. To avoid this, manually call the method that increments the ProgressBar's value using this.Invoke().