cDialogProgress class

A cDialogProgress keeps the state of a long task: a position between a minimum and a maximum, the time spent and an estimate of the time left, a message, and whether the task was cancelled. It raises events when the position changes or the task is cancelled, and describes the state as a line of text.

The class shows no window. In version 1.1.0.0 it has no dialog, no progress bar and no cancel button, whatever its name, Title, ShowCancel, Style and OwnerHwnd suggest. A program that wants to show the progress reads Percentage or GetDescription and draws them itself, for instance on a form, and calls Cancel from its own button. ShowProgress writes the description with Debug.Print, which appears in the debug console of the IDE and nowhere in a built program.

A cDialogProgress is made with New: Dim progress As New cDialogProgress. It cannot be made with CreateObject. To receive the events, declare a variable WithEvents.

Start begins a task. Progress, Increment, PerformStep, SetPercentage and Complete move the position, each raising ProgressChanged. Cancel sets Cancelled, which the task has to test: nothing stops a task by itself, and only SimulateProgress looks at the flag.

Dim progress As New cDialogProgress
Dim i As Long

progress.Message = "Copying files"
progress.Max = 200
progress.Start

For i = 1 To 200
    If progress.Cancelled Then Exit For
    ' ... copy file i ...
    progress.Progress = i
Next i

If Not progress.Cancelled Then progress.Complete
Debug.Print progress.Percentage & "%"

Note

GetRemainingTime, and with it GetRemainingTimeString and GetDescription, raise error 11 (Division by zero) when they are called before the clock has ticked after Start (the clock of Windows ticks about every 16 milliseconds) and Progress is above Min. A program that reports progress as it begins reads the description too early. Call them later, or handle the error.

Note

Percentage and SetPercentage multiply in a Long, so a range wider than about 21 million units can raise error 6 (Overflow): Percentage with Max 100,000,000 and a position of 50,000,000 does, and so does SetPercentage with 50. Use a smaller range, such as the number of items instead of the number of bytes.

Properties

Cancelled

Whether Cancel was called, read-only.

Syntax: object.Cancelled As Boolean

False in a new object. Cancel sets it to True; Start and Reset set it to False.

Dim progress As New cDialogProgress

Debug.Print progress.Cancelled    ' False
progress.Cancel
Debug.Print progress.Cancelled    ' True
progress.Start
Debug.Print progress.Cancelled    ' False

DetailMessage

A second line of text for GetDescription.

Syntax: object.DetailMessage [ = value ]

value
A String. The default is an empty string.

GetDescription adds it on a new line when it is not empty.

Dim progress As New cDialogProgress

progress.Style = ProgressStyle.Marquee
progress.Message = "Copying"
progress.DetailMessage = "report.txt"
Debug.Print Replace(progress.GetDescription(), vbCrLf, " | ")    ' Copying | report.txt

Max

The value of Progress at the end of the task.

Syntax: object.Max [ = value ]

value
A Long. The default is 100.

Changing it does not move Progress. A value that is not above Min makes Percentage 0.

Dim progress As New cDialogProgress

Debug.Print progress.Max    ' 100
progress.Max = 250
Debug.Print progress.Max    ' 250

Message

The main text of GetDescription.

Syntax: object.Message [ = value ]

value
A String. The default is Processing....
Dim progress As New cDialogProgress

Debug.Print progress.Message    ' Processing...

Min

The value of Progress at the start of the task.

Syntax: object.Min [ = value ]

value
A Long. The default is 0.

Start sets Progress to it. Changing it does not move Progress.

Dim progress As New cDialogProgress

progress.Min = 10
progress.Max = 110
progress.Start
Debug.Print progress.Progress      ' 10
Debug.Print progress.Percentage    ' 0

OwnerHwnd

A property that has no effect.

Syntax: object.OwnerHwnd [ = value ]

value
A LongPtr. The default is 0.

The value is stored; there is no window to own. Unlike the other properties, Reset keeps it.

Dim progress As New cDialogProgress

progress.OwnerHwnd = 1234
progress.Reset
Debug.Print progress.OwnerHwnd    ' 1234

Percentage

The position as a percentage of the range, read-only.

Syntax: object.Percentage As Long

(Progress - Min) * 100 \ (Max - Min), rounded down; 0 when Max is not above Min. A position below Min, which Increment can reach with a negative Step, gives a negative number. See the second note above for a range that is too wide.

Dim progress As New cDialogProgress

progress.Progress = 40
Debug.Print progress.Percentage    ' 40
progress.Max = 200
Debug.Print progress.Percentage    ' 20
progress.Max = 0
Debug.Print progress.Percentage    ' 0

Progress

The position of the task.

Syntax: object.Progress [ = value ]

value
A Long from Min to Max.

A value outside the range is ignored, without an error or an event. Any other value, even the current one, is stored and raises ProgressChanged. The default is 0.

Dim progress As New cDialogProgress

progress.Progress = 40
Debug.Print progress.Progress    ' 40
progress.Progress = 150          ' above Max: ignored
Debug.Print progress.Progress    ' 40
progress.Progress = -1           ' below Min: ignored
Debug.Print progress.Progress    ' 40

ShowCancel

Whether ShowProgress adds a line on cancelling.

Syntax: object.ShowCancel [ = value ]

value
A Boolean. The default is True.

With True, ShowProgress ends its text with (Press Ctrl+Break to cancel). The class does not react to that key; a task is cancelled by a call to Cancel.

Dim progress As New cDialogProgress

Debug.Print progress.ShowCancel    ' True

ShowElapsedTime

Whether GetDescription names the time spent.

Syntax: object.ShowElapsedTime [ = value ]

value
A Boolean. The default is True.
Dim progress As New cDialogProgress

Debug.Print progress.ShowElapsedTime    ' True

ShowRemainingTime

Whether GetDescription names the estimated time left.

Syntax: object.ShowRemainingTime [ = value ]

value
A Boolean. The default is True.
Dim progress As New cDialogProgress

Debug.Print progress.ShowRemainingTime    ' True

Step

The amount by which Increment moves Progress.

Syntax: object.Step [ = value ]

value
A Long. The default is 1.

SimulateProgress uses it as the step of its loop.

Dim progress As New cDialogProgress

progress.Step = 25
progress.Increment
progress.Increment
Debug.Print progress.Progress    ' 50

Style

The kind of progress that GetDescription describes.

Syntax: object.Style [ = value ]

value
A ProgressStyle member. The default is ProgressBar.

With ProgressBar, GetDescription adds the percentage and the times. With Marquee or Steps it returns the message alone, with the detail line.

Dim progress As New cDialogProgress

Debug.Print progress.Style = ProgressStyle.ProgressBar    ' True
progress.Style = ProgressStyle.Steps
Debug.Print progress.Style                                ' 2

Title

A property that has no effect.

Syntax: object.Title [ = value ]

value
A String. The default is Progress.

The value is stored. The class shows no title bar and does not use it in its text.

Dim progress As New cDialogProgress

Debug.Print progress.Title    ' Progress

Methods

Cancel

Marks the task as cancelled.

Syntax: object.Cancel

Sets Cancelled to True and raises CancelClicked. It stops nothing: the task has to test Cancelled. Cancelling a task that was already cancelled raises the event again.

Dim progress As New cDialogProgress

progress.Cancel
Debug.Print progress.Cancelled    ' True

Complete

Moves the position to the end.

Syntax: object.Complete

Sets Progress to Max and raises ProgressChanged with that value, also when Progress was already there.

Dim progress As New cDialogProgress

progress.Max = 80
progress.Complete
Debug.Print progress.Progress      ' 80
Debug.Print progress.Percentage    ' 100

GetDescription

Returns the state as a line of text.

Syntax: object.GetDescription ( ) As String

Returns Message. With Style ProgressBar it adds (N%) with Percentage; then - Elapsed: and GetElapsedTimeString when ShowElapsedTime is True, and - Remaining: and GetRemainingTimeString when ShowRemainingTime is True and Progress is above Min. A non-empty DetailMessage follows after vbCrLf. See the first note above for the error that the remaining time can raise.

Dim progress As New cDialogProgress

progress.Message = "Copying"
progress.DetailMessage = "report.txt"
progress.Progress = 50
Debug.Print Replace(progress.GetDescription(), vbCrLf, " | ")
' Copying (50%) - Elapsed: 0:00 - Remaining: 0:00 | report.txt

progress.ShowElapsedTime = False
progress.ShowRemainingTime = False
Debug.Print Replace(progress.GetDescription(), vbCrLf, " | ")
' Copying (50%) | report.txt

GetElapsedTime

Returns the whole seconds since Start.

Syntax: object.GetElapsedTime ( ) As Long

0 before the first Start. The count is made from the clock of Windows, which wraps around to negative numbers after 24.8 days of uptime; while it is negative at Start, the time functions return 0.

Dim progress As New cDialogProgress

Debug.Print progress.GetElapsedTime()    ' 0

GetElapsedTimeString

Returns the time since Start as text.

Syntax: object.GetElapsedTimeString ( ) As String

m:ss, or h:mm:ss from one hour on, such as 0:02 and 1:05:09.

Dim progress As New cDialogProgress

Debug.Print progress.GetElapsedTimeString()    ' 0:00

GetRemainingTime

Estimates the whole seconds that are left.

Syntax: object.GetRemainingTime ( ) As Long

Takes the speed so far, (Progress - Min) per millisecond since Start, and returns the time that the rest of the range needs at that speed. Returns 0 before Start and while Progress is not above Min. It raises error 11 when no time has passed since Start; see the first note above.

Dim progress As New cDialogProgress

Debug.Print progress.GetRemainingTime()    ' 0

GetRemainingTimeString

Returns GetRemainingTime as text.

Syntax: object.GetRemainingTimeString ( ) As String

In the form of GetElapsedTimeString.

Dim progress As New cDialogProgress

Debug.Print progress.GetRemainingTimeString()    ' 0:00

Increment

Moves Progress by Step.

Syntax: object.Increment

Adds Step to Progress and raises ProgressChanged. When the new value is above Max, nothing happens: no change and no event. The new value is not compared with Min, so a negative Step can lead below it.

Dim progress As New cDialogProgress

progress.Step = 40
progress.Increment
progress.Increment
progress.Increment    ' 120 is above Max: ignored
Debug.Print progress.Progress    ' 80

PerformStep

The same as Increment.

Syntax: object.PerformStep

Dim progress As New cDialogProgress

progress.PerformStep
Debug.Print progress.Progress    ' 1

Reset

Restores the starting values.

Syntax: object.Reset

Sets Title to Progress, Message to Processing..., DetailMessage to an empty string, Progress and Min to 0, Max to 100, Step to 1, Style to ProgressBar, the three Show properties to True, Cancelled to False, and forgets Start. It raises no event and leaves OwnerHwnd as it is.

Dim progress As New cDialogProgress

progress.Max = 500
progress.Message = "Importing"
progress.Reset
Debug.Print progress.Max        ' 100
Debug.Print progress.Message    ' Processing...

SetPercentage

Moves Progress to a percentage of the range.

Syntax: object.SetPercentage ( percent )

percent
required A Long from 0 to 100. Another value is ignored, without an error or an event.

Sets Progress to Min + (Max - Min) * percent \ 100, rounded down, and raises ProgressChanged. See the second note above for a range that is too wide.

Dim progress As New cDialogProgress

progress.Min = 10
progress.Max = 110
progress.SetPercentage 25
Debug.Print progress.Progress      ' 35
progress.SetPercentage 101         ' ignored
Debug.Print progress.Progress      ' 35
progress.SetPercentage 100
Debug.Print progress.Progress      ' 110

ShowProgress

Writes the description with Debug.Print.

Syntax: object.ShowProgress

Prints GetDescription, and when ShowCancel is True a blank line and (Press Ctrl+Break to cancel). It opens no window, and a built program shows nothing. It raises the error of GetDescription when that does.

Dim progress As New cDialogProgress

progress.Message = "Working"
progress.Progress = 30
progress.ShowProgress

SimulateProgress

Runs the task from the minimum to the maximum with a pause after every step, to try a program that reacts to the events.

Syntax: object.SimulateProgress ( [ delayMs ] )

delayMs
optional A Long, the pause after every step in milliseconds. The default is 100.

Calls Start, then for i from Min to Max in steps of Step: stops when Cancelled is True, sets Progress to i (raising ProgressChanged), calls ShowProgress and waits delayMs. Unless the task was cancelled it ends with Complete, so the last position is reported twice. The thread waits and does not process messages, so a handler of ProgressChanged is the place to call Cancel. A Step of 0 never ends unless the task is cancelled, and a negative one skips the loop, so that only Complete runs.

With a delayMs of 0 or 1 the call raises error 11 (Division by zero) at its second step, from the estimate of the remaining time; see the first note above. The default of 100 does not.

Dim progress As New cDialogProgress

progress.Max = 5
progress.SimulateProgress 50
Debug.Print "Done at " & progress.Percentage & "%"

Start

Begins a task.

Syntax: object.Start

Sets Progress to Min, Cancelled to False and notes the time for GetElapsedTime and GetRemainingTime. It raises no event, and does not change Message and the other texts.

Dim progress As New cDialogProgress

progress.Progress = 60
progress.Start
Debug.Print progress.Progress    ' 0

Events

CancelClicked

Raised by Cancel.

Syntax: Event object.CancelClicked ( )

The handler runs before Cancel returns, and Cancelled is already True.

ProgressChanged

Raised when the position is set.

Syntax: Event object.ProgressChanged ( value )

value
The new position, a Long.

Raised by Progress for a value inside the range, Increment and PerformStep when they move, SetPercentage for a percentage inside 0 to 100, and Complete; also when the value is the same as before. Start and Reset raise nothing. The handler runs before the call returns.

A class that receives the events declares the variable with WithEvents:

Class ProgressLog
    Private WithEvents mProgress As cDialogProgress

    Public Sub Attach(ByVal source As cDialogProgress)
        Set mProgress = source
    End Sub

    Private Sub mProgress_ProgressChanged(ByVal value As Long)
        Debug.Print "changed to " & value
    End Sub

    Private Sub mProgress_CancelClicked()
        Debug.Print "cancelled"
    End Sub
End Class

This example counts the events of a short task. A value outside the range raises none, Complete raises one, and CancelClicked follows from Cancel:

Dim progress As New cDialogProgress
Dim log As New ProgressLog

log.Attach progress
progress.Max = 3
progress.Start
progress.Increment
progress.Increment
progress.Progress = 9    ' outside the range: no event
progress.Complete
progress.Cancel
' Output:
' changed to 1
' changed to 2
' changed to 3
' cancelled

Enumerations

ProgressStyle

The kinds of progress for Style. Only GetDescription reads it.

Member Value Meaning
ProgressBar 0 A position in a range: the description has the percentage and the times
Marquee 1 A task of unknown length: the message alone
Steps 2 A task in steps: the message alone

Example

This example reports the progress of a task as text every 25 per cent, and stops when the task is cancelled by a call from elsewhere in the program.

Dim progress As New cDialogProgress
Dim item As Long

progress.Message = "Processing items"
progress.Max = 100
progress.Step = 5
progress.Start

For item = 1 To 20
    If progress.Cancelled Then Exit For
    ' ... process item ...
    progress.PerformStep
    If progress.Percentage Mod 25 = 0 Then Debug.Print progress.Percentage & "% done"
Next item

If progress.Cancelled Then
    Debug.Print "Stopped at " & progress.Percentage & "%"
Else
    progress.Complete
    Debug.Print "Finished"
End If

See Also