BackgroundWorker 仅在完成时报告进度

BackgroundWorker only reporting progress when finished

我有一个非常简单的应用程序,可以将包含几千行的 excel 文件加载到 DataGridView 组件中。然后通过按“扫描”按钮扫描此 table 以查找重复项和各种其他问题。我选择在 BackgroundWorker 中执行这项密集任务 运行,因此 UI 保持响应,因此我可以报告其进度。我的 Scan 按钮的代码只是调用后台工作组件上的 RunWorkerAsync() 方法,然后执行此操作:

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

这会调用另一个 class 来完成实际工作,将扫描仪作为参数传递。扫描仪 class 然后执行此操作:

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

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

扫描执行得非常好并产生了预期的输出,但我的 ProgressBar 从未使用以下代码更新 BackgroundWorker 的 ProgressChanged 事件:

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

考虑到这可能是我计算百分比的方式的问题,我替换了对 ReportProgress 的调用并发送了一个值 50 以查看会发生什么。进度条会更新,但只有在整个扫描完成后才会更新!没有出现异常,它的表现就像 UI 线程太忙于做其他事情(据我所知,除了遍历 table 中的每一行之外)。知道为什么我会看到这种行为吗?

******编辑******

我找到了罪魁祸首。我忘记了,在扫描期间,table 的行可以使用工具提示和背景颜色进行更新。我评论了这两行,果然进度条现在工作得很好。这证明了我的理论,即 UI 线程确实不堪重负。有没有办法解决这个问题?进度条更新的某种更高优先级?

对于整数除法,值向零舍入到下一个最接近的整数。 在你的情况下,它总是会在乘以 100 之前向零舍入,因为 row.index 大概在 0 和 table.RowCount - 1 之间。

完全冗长:

row.Index / table.RowCount * 100

可能会变成:

(int)(((double)row.Index / (double)table.RowCount) * 100)

我通过对我的 table 的底层数据源进行处理直到需要显示它的最后一分钟来解决这个问题。这样,在显示 table 之前,不会有 UI 更新。我仍然需要遍历 table 本身以在最后为某些单元格设置背景颜色和工具提示(因为您显然不能在 DataSource 本身上执行此操作)但是与预先在 DataSource 上完成的处理。我的 ProgressBar 现在可以正常工作了。

******编辑******

您实际上可以使用 DataGridView 的 CellFormatting 事件为单元格着色,无需遍历行,这具有在重新排序 table 时保持工具提示和背景颜色的额外效果:

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