Отчет о ходе выполнения задачи Async

Привет, коллеги-разработчики,

Я столкнулся с небольшой дилеммой. У меня есть приложение WPF, которое читает довольно большой файл Excel и после этого выводит его как файл XML. Проблема, с которой я столкнулась, заключается в том, что я хочу сообщить о ходе операции обратно обработчику.

Я не могу заставить его работать «плавно», буквально индикатор выполнения, который находится в streamе GUI, заполняется сразу и сразу же после этого заполняется DataGrid, который содержит содержимое, прочитанное из файла Excel.

Я новичок в Async / Await / Task, я до сих пор использую BackgroundWorker, но я хочу получить больше знаний об этом, поэтому мне жаль, если это глупый вопрос.

Я прочитал учебник от Стивена Клири отсюда.

Я честно понятия не имею, почему Progress Bar не заполняет «гладко», как это должно быть …

Код, вызываемый в GUI:

var progress = new Progress(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()); 

Тело метода ReadFileContent(filePath, progress) :

 public DataTable ReadFileContent(string filePath, IProgress 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; } } - public DataTable ReadFileContent(string filePath, IProgress 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; } } 

Заранее спасибо !

Во-первых, давайте избавимся от опасного вызова ContinueWith . Вы действительно должны использовать await вместо этого:

 var progress = new Progress(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; 

Затем проблема, с которой вы, вероятно, видите прогресс, заключается в том, что обработчик Progress ожидает progressPercent , но ReadFileContent отправляет количество строк, которые он читает, а не процент. Итак, чтобы исправить это:

 if (currentIndex % percentageProgress == 0) progress?.Report(row * 100 / totalRows); 

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

индикатор прогресса не заполняется «гладко», как это должно быть

То, что я описал выше, является минимально приемлемым решением. В частности, код «только обновление 10 раз» немного проблематичен; он всегда обновляется 10 раз, независимо от того, насколько быстро или медленнее обновления. Более общим решением было бы использовать решение IProgress основе IProgress , которое позволит вам дросселировать в зависимости от времени (например, 4 обновления в секунду).