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.
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.
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.
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.
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.
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).
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.
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.
- In the owner class, embed a member variable which is a CProgressWnd object.
- 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).
- As each step of the process completes, call UpdateProgress() to fill the bar.
- 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:
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 (firstname.lastname@example.org).
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.