Introduction
To use the CWaitCursor
class to show a wait cursor, an instance of the object is simply created. Destroying the object restores the normal cursor. However, the documentation states that although the lifetime of the object may span messages, the wait cursor is only guaranteed to remain on the screen for the duration of the currently processing message.
The purpose of this article is to describe a simple and neat method of allowing the wait cursor to persist across messages.
Background
The reason the wait cursor is not guaranteed to persist across messages is that as well as our messages using the wait cursor, the WM_SETCURSOR
message may also be processed. The default processing of this message will cause the cursor to change. To prevent this from happening, WM_SETCURSOR
needs to be handled, and if the wait cursor is to be shown, we need to call CCmdTarget::RestoreWaitCursor()
, thus :
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (WaitCursorShown())
{
RestoreWaitCursor();
return TRUE;
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
WaitCursorShown()
is simple enough - all we need to do when we create the CWaitCursor
is to set a variable to indicate this, and then of course, set the variable again to indicate the destruction of the CWaitCursor
. However, this has the potential to get a little messy, particularly as we may well need to implement this in a number of windows (for example, CFrameWnd
and CView
derived classes). It is also rather error prone, as we have to make sure we update the variable accordingly wherever the CWaitCursor
object is destroyed, because it goes out of scope.
CPersistentWaitCursor
CPersistentWaitCursor
is a very simple class derived from CWaitCursor
. Construction of a CPersistentWaitCursor
object increments a reference count, to indicate that the wait cursor is to be shown. Similarly, destruction of the object decrements the reference count. Being derived from CWaitCursor
, the constructor and destructor cause the wait cursor to be shown and hidden appropriately.
CPersistentWaitCursor
offers only one more method than CWaitCursor
, CPersistentWaitCursor::WaitCursorShown()
. This returns a bool
, to indicate whether or not the wait cursor should be shown, based on the value of the reference count.
Using CPersistentWaitCursor
is very simple :
#include "PersistentWaitCursor.h"
void CMyWnd::DoSomeLengthyOperation()
{
CPersistentWaitCursor waitCursor;
...
}
BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (CPersistentWaitCursor::WaitCursorShown())
{
RestoreWaitCursor();
return TRUE;
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
Reference counting is performed using a single instance of CPersistentWaitCursorMonitor
, a simple class which maintains the count. Access to the count is protected using a critical section.
Conclusion
Because of the simplicity of the class, I have not included a demonstration of it's use.
The class has found use in the application I am currently developing. This application is very similar in style to Windows Explorer. Items in the tree represent rows in a database. Expanding these items performs two jobs - first, child items are loaded from the database and placed in the tree, and secondly data is taken from the child items, processed, and placed into a view. Both the loading of the database items (a function of the tree), and the processing (a function of the document / view) are lengthy operations, but appear to the user as single operation. CPersistentWaitCursor
simplifies showing and managing the wait cursor whilst these operations take place.
History
- 27th January 2003 - Updated to correct compiler error in source file (included non-existent file!)