I have a very simple app that loads an excel file with several thousand rows into a DataGridView component. This table is then scanned for duplicates and various other issues by pressing the Scan button. I chose to have this intensive task run in a BackgroundWorker so the UI remains responsive and so I can report its progress. The code for my Scan button simply calls the RunWorkerAsync() method on the background worker component, which then does this :
Scanner scanner = new Scanner(this, dgvScancodes, dgvErroredScancodes, BgwScanner);
This calls another class to do the actual work, passing the scanner as a parameter. The Scanner class then does this :
foreach (DataGridViewRow row in table.Rows)
{
//Long computations on each row
worker.ReportProgress(row.Index / table.RowCount * 100);
}
The scan executes perfectly fine and produces the expected output but my ProgressBar never updates with the following code on the BackgroundWorker's ProgressChanged event :
private void BgwScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbProgress.Value = e.ProgressPercentage;
}
Thinking this could be an issue with the way I calculate the percentage, I replaced my call to ReportProgress and just sent a value of 50 to see what would happen. The progress bar does update, but only when the entire scan is completed! No exception is raised and it's behaving like the UI thread is too busy doing other things (which, aside from looping over every row in a table, it is not to the best of my knowledge). Any idea why I'm seeing this behavior?
******EDIT******
I found my culprit. I forgot that, during the scan, the table's rows can be updated with a tooltip and a background color. I commented these 2 lines and sure enough the progress bar works perfectly fine now. This proves my theory that indeed the UI thread is being overwhelmed. Is there potentially a way around this? Some sort of higher priority for the progress bar updates?
I addressed this issue by doing the processing on the underlying DataSource for my tables until the very last minute when it needs to be displayed. This way there are no UI updates until the table is displayed. I still need to iterate over the table itself to set the background color and tooltip for certain cells at the end (since you obviously can't do this on the DataSource itself) but that takes a relatively short time compared to the processing that is being done on the DataSource beforehand. My ProgressBar now works properly.
******EDIT******
You can actually use the DataGridView's CellFormatting event to color cells, no need to iterate over the rows and this has the bonus effect of keeping the tooltip and the background color when re-ordering the 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();
}
...
}