Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
589 views
in Technique[技术] by (71.8m points)

c# - Reporting progress from Async Task

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 !

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...