BackgroundWorker только сообщает о прогрессе по завершении - PullRequest
0 голосов
/ 18 марта 2020

У меня очень простое приложение, которое загружает файл Excel с несколькими тысячами строк в компонент DataGridView. Затем эту таблицу сканируют на наличие дубликатов и других проблем, нажав кнопку «Сканировать». Я решил запустить эту интенсивную задачу в BackgroundWorker, чтобы пользовательский интерфейс оставался отзывчивым, чтобы я мог сообщать о его ходе. Код для моей кнопки «Сканирование» просто вызывает метод RunWorkerAsyn c () в фоновом рабочем компоненте, который затем делает следующее:

Scanner scanner = new Scanner(this, dgvScancodes, dgvErroredScancodes, BgwScanner);

Это вызывает другой класс для выполнения фактической работы, передавая сканер как параметр. Затем класс Scanner делает это:

foreach (DataGridViewRow row in table.Rows)
{
    //Long computations on each row

    worker.ReportProgress(row.Index / table.RowCount * 100);
}

Сканирование выполняется отлично и выдает ожидаемый результат, но мой ProgressBar никогда не обновляется с помощью следующего кода в событии ProgressChanged BackgroundWorker's:

private void BgwScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pgbProgress.Value = e.ProgressPercentage;
}

Думая, что это может быть проблемой с тем, как я вычисляю процент, я заменил свой вызов ReportProgress и просто отправил значение 50, чтобы посмотреть, что произойдет. Индикатор выполнения обновляется, но только после полного сканирования! Никаких исключений не возникает, и он ведет себя так, будто поток пользовательского интерфейса слишком занят выполнением других задач (что, помимо циклического повторения каждой строки таблицы, насколько мне известно, не является этим). Есть идеи, почему я вижу это поведение?

****** РЕДАКТИРОВАТЬ ******

Я нашел своего виновника. Я забыл, что во время сканирования строки таблицы могут быть обновлены с помощью всплывающей подсказки и цвета фона. Я прокомментировал эти 2 строки и, конечно же, индикатор выполнения теперь работает отлично. Это подтверждает мою теорию, что поток пользовательского интерфейса действительно перегружен. Есть ли способ обойти это? Какой-то более высокий приоритет для обновлений индикатора выполнения?

Ответы [ 2 ]

1 голос
/ 18 марта 2020

Для целочисленного деления значение округляется от нуля до следующего ближайшего целого числа. В вашем случае он всегда округляется до нуля, а затем умножается на 100, поскольку row.index предположительно находится между 0 и таблицей.RowCount - 1.

Будучи полностью многословным:

row.Index / table.RowCount * 100

Может стать:

(int)(((double)row.Index / (double)table.RowCount) * 100)
0 голосов
/ 19 марта 2020

Я решил эту проблему, выполняя обработку базового источника данных для моих таблиц до самой последней минуты, когда он должен отображаться. Таким образом, обновления таблицы не отображаются до тех пор, пока не отобразится таблица. Мне все еще нужно перебрать саму таблицу, чтобы установить цвет фона и всплывающую подсказку для определенных ячеек в конце (поскольку вы, очевидно, не можете сделать это на самом DataSource), но это занимает относительно короткое время по сравнению с обработкой, которая выполняется сделано на DataSource заранее. Мой ProgressBar теперь работает правильно.

****** РЕДАКТИРОВАТЬ ******

Вы можете использовать событие CellFormatting объекта DataGridView для окрашивания ячеек, не нужно перебирать строки и это дает дополнительный эффект сохранения всплывающей подсказки и цвета фона при повторном упорядочении таблицы:

private void dgvErroredScancodes_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    DataGridViewCell scancodeCell = dgvErroredScancodes.Rows[e.RowIndex].Cells[1];

    if (e.Value.Equals((int) ErrorType.DUPLICATE))
    {
        scancodeCell.Style.BackColor = ErrorType.DUPLICATE.BackgroundColor();
        scancodeCell.ToolTipText = ErrorType.DUPLICATE.ErrorMessage();
    }
    ...
}
...