I have a WinForms application containing Button and a RichTextBox controls. After user clicks on Button, an IO demanding operation is executed. To prevent blocking of the UI thread I have implemented the async/await pattern. I would also like to report progress of this operation into RichTextBox. This is how the simplified logic looks like:
private async void LoadData_Click(Object sender, EventArgs e)
{
this.LoadDataBtn.Enabled = false;
IProgress<String> progressHandler = new Progress<String>(p => this.Log(p));
this.Log("Initiating work...");
List<Int32> result = await this.HeavyIO(new List<Int32> { 1, 2, 3 }, progressHandler);
this.Log("Done!");
this.LoadDataBtn.Enabled = true;
}
private async Task<List<Int32>> HeavyIO(List<Int32> ids, IProgress<String> progress)
{
List<Int32> result = new List<Int32>();
foreach (Int32 id in ids)
{
progress?.Report("Downloading data for " + id);
await Task.Delay(500); // Assume that data is downloaded from the web here.
progress?.Report("Data loaded successfully for " + id);
Int32 x = id + 1; // Assume some lightweight processing based on downloaded data.
progress?.Report("Processing succeeded for " + id);
result.Add(x);
}
return result;
}
private void Log(String message)
{
message += Environment.NewLine;
this.RichTextBox.AppendText(message);
Console.Write(message);
}
After operation gets successfully completed, the RichTextBox contains following text:
Initiating work...
Downloading data for 1
Data loaded successfully for 1
Processing succeeded for 1
Downloading data for 2
Data loaded successfully for 2
Processing succeeded for 2
Downloading data for 3
Done!
Data loaded successfully for 3
Processing succeeded for 3
As you can see the progress for 3rd work item is reported after Done!.
My question is, what is causing the delayed progress reporting and how can I achieve that flow of LoadData_Click will continue only after all progress has been reported?
IProgress<String>&Progress? Where is the source for them? They seem pretty important to your question.progress?.Report(is pushing the execution of thep => this.Log(p)on to theSychronizationContext. That means it has to wait until the UI message loop is idle before it can execute that code. I suggest you try to remove theprogresscalls and write to the log directly. I suspect the problem will go away then.