Hello fellow developers,
I am facing a slight dilemma. I have a WPF application that reads an rather large Excel File and after that it outputs it as an XML file. The problem that I am facing is that I want to report the progress of the operation back to the handler.
I cannot get it to work "smoothly", literally the Progress Bar that is in the GUI Thread gets filled at once and immediately after that the DataGrid that holds the content read from the Excel File gets filled.
I am new to Async/Await/Task, I've been using BackgroundWorker until now but I wish to gain more knowledge about this, so I'm sorry if this is a dumb question.
I have read the tutorial from Stephen Cleary from here.
I honestly have no idea why the Progress Bar does not fill "smoothly" as it should be...
Code that is getting called in the GUI:
var progress = new Progress<int>(progressPercent => pBar.Value = progressPercent);
Task.Run(() => _sourceService.ReadFileContent(filePath, progress)).ContinueWith(task =>
{
dataGrid.ItemsSource = task.Result.DefaultView;
DataTable = task.Result;
if (DataTable.Rows.Count > 0)
BtnCreateXmlFile.IsEnabled = true;
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
The method body of ReadFileContent(filePath, progress)
:
public DataTable ReadFileContent(string filePath, IProgress<int> progress)
{
var rowStart = 7;
var columnStart = 1;
var existingFile = new FileInfo(filePath);
using (var package = new ExcelPackage(existingFile))
{
var worksheet = package.Workbook.Worksheets["BOP"];
var dt = new DataTable();
// Compose the name of the table from:
// - Product Type
// - Customer
// - Customer Project
// * These can be found in the "CollaborationContext" sheet that is present in the ExcelFile.
if (package.Workbook.Worksheets["CollaborationContext"].SelectedRange["B6"].Value.Equals("object_type"))
{
dt.TableName = package.Workbook.Worksheets["CollaborationContext"].SelectedRange["C9"].Value + " " +
package.Workbook.Worksheets["CollaborationContext"].SelectedRange["D9"].Value + " " +
package.Workbook.Worksheets["CollaborationContext"].SelectedRange["E9"].Value;
}
dt.TableName = package.Workbook.Worksheets["CollaborationContext"].SelectedRange["B9"].Value + " " +
package.Workbook.Worksheets["CollaborationContext"].SelectedRange["C9"].Value + " " +
package.Workbook.Worksheets["CollaborationContext"].SelectedRange["D9"].Value;
// Report only in chunks. We do not want to call `progress.Report` for each row that is read.
var totalRows = worksheet.Dimension.End.Row;
var currentIndex = 0;
var percentageProgress = totalRows / 10;
// Get Columns and add them to the DataTable
for (var col = columnStart; col <= worksheet.Dimension.End.Column - 1; col++)
dt.Columns.Add(worksheet.Cells[6, col].Value.ToString());
// Place data into DataTable
for (var row = rowStart; row <= worksheet.Dimension.End.Row; row++)
{
var dr = dt.NewRow();
var x = 0;
currentIndex++;
for (var col = columnStart; col <= worksheet.Dimension.End.Column - 1; col++)
{
dr[x++] = worksheet.Cells[row, col].Value;
}
dt.Rows.Add(dr);
// Report progress back to the handler
if (currentIndex % percentageProgress == 0)
progress?.Report(row);
}
return dt;
}
}
Thank you in advance !
First, let's get rid of the dangerous ContinueWith
call. You really should use await
instead:
var progress = new Progress<int>(progressPercent => pBar.Value = progressPercent);
var result = await Task.Run(() => _sourceService.ReadFileContent(filePath, progress));
dataGrid.ItemsSource = result.DefaultView;
DataTable = result;
if (DataTable.Rows.Count > 0)
BtnCreateXmlFile.IsEnabled = true;
Next, the problem you're likely seeing with progress is that your Progress<int>
handler is expecting a progressPercent
, but ReadFileContent
is sending how many rows it's read, not a percentage. So, to fix that:
if (currentIndex % percentageProgress == 0)
progress?.Report(row * 100 / totalRows);
(there are several other options here; e.g., you could decide to report the current row and the total rows if you want a fancier UI).
the Progress Bar does not fill "smoothly" as it should be
What I've described above is the minimum acceptable solution. In particular, the "only update 10 times" code is a bit problematic; it always updates 10 times, regardless of how fast or slow the updates are. A more general solution would be to use an Rx-based IProgress
solution, which would allow you to throttle based on time (e.g., 4 updates per second).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With