Popup Progress window (2)

CProgressWnd provides a full-featured window for
showing the progress of an operation. It supports a progress bar
with a configurable edge style and fill colour, a message to let
the user know what is happening, an optional cancel mechanism and
an optional animation in the form of an AVI. The window
automatically sizes itself to fit all the components on by
changing its height so that the message can fit into the space to
the right of the animation (if required), along with the progress
bar and the cancel button (if required). Some parameters can be
changed while the window is showing, in which case the window
adjusts accordingly (unless specified), other parameters can only
be changed while the window is hidden. The window is operated as
a modeless window (i.e., it does not block input to other
windows).

The cancel mechanism can operate in two ways; you can poll the
window to see if it was cancelled, or you can get it to notify
you when it has been cancelled (by setting a non-zero
notification message number). Which you use is up to you, but the
two methods have their uses: if your program shows the window,
performs a loop of code and then hides it in one routine, then
use the poll method (to stop the loop); if your program shows the
progress window and then returns to the main message loop, use
the notification method to know when to stop using it and hide
it. The window does not hide itself when cancelled; when the code
which created it detects that it has been cancelled, it is
responsible for hiding it.

The window does not have any parent; this is necessary because
if you tried to create it as a child of an MDI or SDI main frame
window, it would conflict with the MDI / SDI mechanism and cause
undesirable behaviour. You can, however, specify an owner
window when you show the progress window; if one is specified,
the progress window will centre itself to that owner window’s
client area (but not if the window is already visible when a
resize takes place), and will notify that owner when it is
cancelled (if you have set the message number to use for this
notification). If one is not specified, the window will centre
itself to the screen (but not if the window is already visible
when a resize takes place), and you cannot use the cancellation
notification (but you can still poll it).

If the AVI filename is specified, but for some reason cannot
be loaded and played, a small space will be left where it would
have been, as a placeholder. The AVI, when played, is played with
a transparent background, and without sound (rules for CAnimateCtrl
apply).

Although this window is based on CFrameWnd, the default
CFrameWnd behaviour of deleting its wrapper object when
destroyed has been overriden and prevented. This is because it
might be desirable to keep the progress window object in
existence and use it multiple times; you can safely embed a CProgressWnd
object (as opposed to a pointer to one) in a class. If you embed
a pointer, you must explicitly delete it to prevent memory leaks.

All parameters that you can change (except the owner window,
which you supply each time you show it) are remembered even after
the window has been hidden again. This means that you only need
to set it up once, and after that it is just a case of changing,
for example, the full scale value and message, and then showing
it again. If you want to change parameters from their previous
values, you must do so explicitly before showing.

Supported Methods:

void SetAnimationFile(CString strAVI)
Sets the filename of the AVI file to use for the animation. If
you no longer want an animation, specify an empty string
(""). This filename can (and should) include a full
drive and pathname if the AVI file is not in the current
directory. The default is no animation. This parameter cannot be
changed while the window is showing.

void SetBarStyle(UINT uStyle)
Changes the way the edges of the bar are drawn. Valid parameter
values are PW_BARSTYLE_FLAT, PW_BARSTYLE_SUNKEN and PW_BARSTYLE_RAISED. The default is PW_BARSTYLE_SUNKEN. This parameter
cannot be changed while the window is showing.

void SetCanCancelFlag(BOOL bCanCancel)
Says whether the cancel button is to be shown or not, and
therefore whether the progress can be halted (the <Esc> and
<Enter> keys can also be used to activate this, as long as
the button is showing). The default is no cancel button, since
progress is usually just an indication rather than an interactive
event. This parameter cannot be changed while the window is
showing.

void SetCancelMessage(UINT uMessage)
Specifies the notification message number to use if the progress
window is cancelled. If it is zero, notification is turned off.
Note that this parameter is irrelevant if no owner window is
specified, or the cancel button is not showing. The default is
zero. This parameter can be changed at any time.

void SetCancelText(CString strText)
By default, the cancel button displays "Cancel" if
shown. If you wish to change this text (for foreign-language
support, for example), use this method. This parameter can be
changed at any time, and the button will be resized as necessary.
Be careful about the length of the string you use – the width of
the window will not change, so if the text is too long the cancel
button will disappear off either side of the window!

void SetFillColour(COLORREF dwColour)
By default, the bar is filled with the colour used to highlight
menu items, or selected items in list boxes or edit boxes. This
specifies the colour to be used for filling the bar, and can be PW_FILLCOLOUR_DEFAULT to restore the
default colour. The actual colour used will be the closest
available match in the current system palette. This parameter can
be changed at any time, and the bar will be redrawn in the new
colour and continue in that colour. This may be useful if you
wish to use different colours for different operations, or if you
wish to change the bar colour as it gets close to the end of its
run.
(I thought about using American / Australian English spellings
for the methods and parameters, as the MFC uses American; but I
decided that as the code was written in England, it should retain
British English spellings. Sorry, folks!)
.

void SetFullScale(UINT uFullScale)
Sets the full-scale value for the progress bar. If you specify 0,
the debug version will assert; the release version will treat the
progress as full-scale permanently rather than crashing with a
divide-by-zero. The default value is 100, representing percent
complete. This parameter can be changed at any time, and the bar
will be cleared and redrawn correctly to reflect the new current
progress value versus the full-scale value. This can be useful if
you don’t know the actual target value to begin with – you can
set a best guess, and then adjust the total while it is running.

void SetMessage(CString strMessage, BOOL bResizeToFit = TRUE)

Sets the message which appears above the progress bar. The
default is an empty string. This parameter can be changed at any
time, and the window will be resized to fit this new message in
(unless you specify FALSE for bResizeToFit). It is recommended that
you allow the window to be resized, because if the new message
takes up more space than is currently available, it will be
clipped. You should only prevent this if you are sure it will
fit, or if you really have serious problems with flicker caused
by the resize (unlikely). Note that if no animation is specified,
this message will take up the full width of the progress window;
otherwise, it fits into the gap left by the animation. Carriage
return / line feed pairs within the message are supported and
obeyed.

void SetTitle(CString strTitle)
Sets the title which appears in the progress window’s caption
bar. The default is "Progress". This parameter can be
changed at any time. This is the only component which is not
taken into account when resizing the window; do not make the
title too long, otherwise it will get clipped.

void SetWidth(UINT uWidth)
By default the window is one quarter of the screen width, or 200
pixels, whichever is greater. Use this method to override this
width (for example, if you have a rather long message, you may
want to make the window wider to prevent it from becoming too
high). The window can be made narrower than the default, at your
own risk. It is not recommended that you go any narrower. This
value can be PW_WIDTH_DEFAULT,
which resets the width to the initial, default value. This
parameter can be changed at any time, and the window will be
resized accordingly. Note that normally, the window never changes
width, only height – even if the combined width of the animation
and message is narrower than the window width.

void Show(CWnd *pWndOwner = NULL)
Causes the window to be created and shown, if not already. Note
that the actual Windows component (HWND)
does not exists while the progress window is hidden, so do not
attempt to perform any window operations on it until you have
called this method. The parameter specifies which window will be
used to initially centre the progress window, and which window
will receive notifications if the progress window is cancelled,
and can be NULL if this is not
required (it is recommended that you own and operate the progress
window from your application’s main frame window, and pass its
pointer as the owner. Although multiple progress windows can
exist, it is more common to have just one).

Do not attempt to change the window’s owner or give it a
parent using the standard CWnd::SetOwner()
and CWnd::SetParent() methods; if
you do, it will cause unpredictable behaviour. You can safely
move the window (from code or as a user) after it has been shown
if you don’t want the default positioning; you can also resize it
from code, although you should not as it defeats the object of
auto-sizing.

BOOL IsShowing(void)
Says whether the window has been created and shown yet. Useful to
prevent the progress window from being reshown while still
active, although that will do no harm. You should not use CWnd::ShowWindow() to make the progress
window invisible; if you do, IsShowing()
will still return TRUE if the
window exists, even though it is not visible. Making the window
invisible in this way will also cause it to snap back to the
centre of the owner window / screen if any parameter change
causes a resize.

BOOL WasCancelled(void)
Indicates whether the window has been cancelled. It will always
return FALSE if the cancel button
(or the window itself) is not showing. Note that you are
responsible for hiding the window if this returns TRUE.

void Hide(void)
Causes the progress window to be hidden and destroyed. Parameters
will be remembered and the wrapper object will still exist, but
not the Windows component (HWND).

void ResetProgress(void)
Resets the current count to 0, and clears the bar.

void UpdateProgress(UINT uCount = 1)
Increments the current value by the amount specified (but not
beyond the full-scale value), and updates the bar as necessary.
Use successive calls to this to fill the bar.

UINT GetCurrentPercentScale(void)
Returns the percent of full-scale completed so far. The return
value is (UINT)-1 if the progress
window is not showing, or full-scale has been set at 0
(indicating an error). When combined with SetFillColour() above, this is useful
to change the colour of the filled bar to reflect getting near
the end of the run.

Using CProgressWnd:

  1. In the owner class, embed a member variable which is a CProgressWnd
    object.
  2. When the progress window is required, set the parameters
    to use, including the name of the AVI file and whether a
    cancel button should be shown or not, and call the Show() method with the owner
    window’s pointer (if required).
  3. As each step of the process completes, call UpdateProgress() to fill the
    bar.
  4. When the progress window is no longer required, or when
    notified that the window has been cancelled (or you have
    polled to find this out), call the Hide() method.

It’s that simple!

Handling The Cancellation Notification From CProgressWnd:

You will have to add the handler manually to the owner window
if you want to make use of the cancellation notification. Here is
how:

1) In the owner class’ message map (code file), add a line:

ON_MESSAGE(cancel_message_number,
member_function_name)

2) In the owner class’ header file, in the
message-map-generated section, add a line:

afx_msg LRESULT member_function_name(WPARAM wParam, LPARAM lParam);

3) Finally, define the function in the owner class’ code file
(without the afx_msg).

The wParam and lParam parameters are not used.

If you have any comments, or you want extra functionality,
e-mail me (jteagle@geocities.com).

Using The Demo

When the program is run, click ‘Show Progress’. Move the mouse
around a bit as instructed to show the bar filling up. Move the
progress window to a different location to verify that a change
in size does not change the position when already visible. Now
double-click the window as instructed to change the progress
window parameters. This demonstrates the use of the notification
version of the cancel mechanism. ‘Hide Progress’ will hide the
window, after which you can click ‘Show Progress’ again. You do
not need to hide the progress window before closing the
application.

Downloads

Demo project
(progress_wnd2_demo.zip, 191K)

Source code
(progress_wnd2_src.zip, 6K)

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read