This is how I have approached this problem. There are a few points I find important.
- Possibility to subdivide the progress into parts. It seem fairly common to either have separate phases in the work, or that each work item takes a considerable amount of time, so each work item should also report progress.
- I do not want to worry about the update frequency of the GUI when writing the code for the algorithm. So reporting progress should ideally be just incrementing an integer. The GUI class should create a timer to poll the progress every second or so.
Therefore I end up with something like this
public interface IProgress {
double Completed { get; } // From 0 to 1
}
public class MyProgress : IProgress{
private IProgress[] subprogresses;
public double Completed
=>// Sums all the subprogresses, divided by the count.
public MyProgress[] Split(int count) {
// creates new MyProgress objects, sets them as subprogresses, and returns the created objects
}
public MyCountingProgress Counting(int totalCount) {
// Creates a counting progress and sets it as the only subprogress
};
}
public class MyCountingProgress : IProgress
{
public MyCountingProgress(int totalCount)
{
TotalCount = totalCount;
}
public int Count;
public int TotalCount { get; }
public double Completed => this.Count / (double)this.TotalCount;
}
Calling would typically look something like this
var progress = new MyProgress();
Task longRunningTask = DoWork(progress);
MyProgressDialog.ShowDialog("please wait", longRunningTask, progress);
The progress dialog starts a timer to update the progress bar, registers a continuation on the task to close the dialog once it is completed (use TaskScheduler.FromCurrentSynchronizationContext() to ensure this runs on the gui thread), and shows a modal dialog.
Some further points:
- You might want to include a description text for the current phase or work item.
- you might want to include a cancellationToken in the progress, or keep it separate.
- you can create a linq decorator that reports progress for each item in a list that is iterated.
- you might want to include a weight for subprogresses, in case you know part of the work takes longer time than others.
- you might want a separate property to indicate that a progress object is completed. The reason is that summation of floats might not sum to exactly 1.0.