ProgressDescriptor

An object-oriented pattern to report progress in a standard way.

Objectives

  • All tools should be able to report progress through the same mechanism.

  • Support parallel or sequencial processes.

  • Support cases where percentage is clear and cases where there is only status for each task.

  • Support different levels of breakdown details (a unique task, vs multiple levels of subtasks)

Design

Composite pattern of ProgressDescriptors. Where they all implement the same interface:

public interface IProgressDescriptor
{
    event ProgressChangedEventHandler ProgressChanged;
    string Title { get; }
    float Percentage { get; }
    ProgressStatus Status { get; }
    string CurrentAction { get; }
    IEnumerable<IProgressDescriptor> SubProgressDescriptors { get; }
}

Interface members:

  • Title: the name of the represented process.

  • Status Members: percentage, status and current action

  • Event: whenever a status member changes, the ProgressChanged event will be raised so that any observer is notified.

  • SubProgressDescriptors: the child progress descriptors.

The composite patterns allow the creation of trees of progress descriptors. As in the following example:

  • CompoundProgressDescriptor:

    • (Green squares)

    • Broken down into several sub-processes. All their status data is calculated automatically, based on the data from their child descriptors.

  • SingleProgressDescriptor:

    • (Blue rounded rectangles)

    • Represent the progress for the atomic processes. They have their own data about their status and percentage. This data should be changed explicitly as their corresponding processes advance.

  • Root Node: the main progress descriptor for the whole process. If only one percentage should be shown, it would the one from the root node.

  • Weights: every reference from a compound to a child node has a weight that will be used to calculate automatically the progress of the parent.

  • Event Propagation: whenever a ProgressDescriptor is updated, it will raise an event to its parent. This events will be propagated updating all the tree upwards until it reaches the root node. When the root node raises its event, it will be handled by the Controller or any other object that is observing.

Note: only SingleProgressDescriptor (leaves) will get their status changed explicitly. All other statuses will be calculated automatically.

Compound and Single ProgressDescriptor Classes

The CompountProgressDescriptor is build from the begining with its child elements. (See "SubProgressDescriptor" below) It doesn't allow to modify its status members since they are automatically calculated.

public class CompoundProgressDescriptor : IProgressDescriptor
{
    public CompoundProgressDescriptor(string title, IEnumerable<SubProgressDescriptor> subProgressDescriptors = null)
    public event ProgressChangedEventHandler ProgressChanged;
    public string Title { get; }
    public float Percentage { get; private set; }
    public ProgressStatus Status { get; private set; }
    public string CurrentAction { get; private set; }
    public IEnumerable<IProgressDescriptor> SubProgressDescriptors => this.subProgressDescriptors.Select(x => x.Progress);        
}

The SingleProgressDescriptor doesn't have children. It has methods that allow updating any subset of its status members (percentage, progress status and current action). Whenever one of these methods is invoked, the data will be updated and the ProgressChanged even will be raised and propagated upwards.

public class SingleProgressDescriptor : IProgressDescriptor
{
    public SingleProgressDescriptor(string title)
    public event ProgressChangedEventHandler ProgressChanged;
    public string Title { get; }
    public float Percentage { get; private set; }
    public ProgressStatus Status { get; private set; }
    public string CurrentAction { get; private set; }
    public IEnumerable<IProgressDescriptor> SubProgressDescriptors => Enumerable.Empty<IProgressDescriptor>();

    public void UpdateState(float percentage) => this.Update(percentage, null, null);
    public void UpdateState(string currentAction) => this.Update(null, currentAction, null);
    public void UpdateState(ProgressStatus status) => this.Update(null, null, status);
    public void UpdateState(float percentage, string currentAction) => this.Update(percentage, currentAction, null);
    public void UpdateState(float percentage, ProgressStatus status) => this.Update(percentage, null, status);
    public void UpdateState(string currentAction, ProgressStatus status) => this.Update(null, currentAction, null);
    public void UpdateState(float percentage, string currentAction, ProgressStatus status) => this.Update(percentage, currentAction, status);
}

SubProgressDescriptor

Used only in the constructor of a CompoundProgressDescriptor, to represent a child progress descriptor and its weight.

    public struct SubProgressDescriptor
    {
        public float Weight { get; set; }
        public IProgressDescriptor Progress { get; set; }
    }

Last updated