C# BackGroundWorker с обновлениями ProgressBar после завершения процесса

У меня есть следующее в событии нажатия кнопки:

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);
  }

}

Мой 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");
      }
    }

  }

}

Моя кнопка отмены:

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

Мой 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);
}

Мой backgroundWorker1_ProgressChanged:

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

Я получаю следующий вывод в окне отладки, когда обрабатываю 2 элемента:

backgroundWorker1_dowork=0

backgroundWorker1_dowork=0

прогрессбар1.значение=1

прогрессбар1.значение=2

Событие запускается, но, как видно из console.write, оно происходит ПОСЛЕ завершения потока. Я получаю прокрутку индикатора выполнения, но только после завершения работы.

Что я сделал не так в этом? Я бы хотел, чтобы он обновлялся по мере завершения каждого элемента.


person ErocM    schedule 19.04.2011    source источник
comment
Это не имеет никакого отношения к вашей реальной проблеме, но я заметил, что вы дважды проверяете ds.SubmitToACH в своем результате LINQ qry.   -  person Cᴏʀʏ    schedule 19.04.2011


Ответы (1)


Это связано с тем, как работают потоки. ProgressChange вызывается в потоке пользовательского интерфейса с помощью BeginInvoke и, следовательно, в другом потоке. Тем временем рабочий поток продолжает работать. Поскольку работы не так много, BackgroundWorker завершает свою работу до того, как BeginInvoke фактически вызовет метод, поскольку переключение потоков происходит не при каждой операции ЦП. Они случаются через некоторое время. Чтобы избежать этого, вручную вызовите метод, который увеличивает значение ProgressBar с помощью this.Invoke().

person Ry-♦    schedule 19.04.2011
comment
Это было то, что мне было нужно! Спасибо! Для тех, кто ищет, вот ссылка на вызов на сайте Microsoft, где я использовал их пример, и он работал отлично: msdn.microsoft.com/en-us/library/zyzhdc6b.aspx - person ErocM; 19.04.2011
comment
Пробовал это и почти каждый ответ, который я нашел здесь, и до сих пор не радуюсь. Мой индикатор выполнения просто ждет, пока рабочий поток не завершится, а затем бум - он обновляется. - person Bertie; 11.02.2021
comment
@Bertie: У этого есть две вероятные причины: либо поток пользовательского интерфейса также выполняет работу, либо о прогрессе не сообщается. Вы можете различать их, пытаясь взаимодействовать с пользовательским интерфейсом. Отвечают ли другие кнопки на форме, когда вы ожидаете, что индикатор выполнения будет двигаться? Управление окном? После того, как вы с этим разобрались, хорошим следующим шагом было бы использование отладчика: точка останова при отчете о ходе выполнения в последнем случае, пауза во время работы, чтобы узнать, что делает основной поток в первом. - person Ry-♦; 12.02.2021
comment
@Ry- После долгих экспериментов - в основном, делая то, что вы предложили, у меня все равно был тот же результат. О прогрессе сообщалось, кнопки и элементы управления окнами реагировали, отладка доказала, что все wws вызывается тут же. Теперь у меня была логика в отдельном методе DoWork. В крайнем случае я вырезал и вставил код в метод DoWork, скомпилировал, запустил... Боб, твой дядя, обновил как надо. Я переместил его обратно в его собственный метод, и он снова перестал работать. По сути, код ДОЛЖЕН быть в методе DoWork. - person Bertie; 13.02.2021