Simple Add-On Wait Dialog in MFC

Despite the prediction of fast end of MFC due to release of .NET framework, it seems MFC is not about to leave us anytime soon. Personally, in many cases, I prefer it to .NET.

Some time ago I found this jewel I’d like to share with you. The original article has been posted by Jeff Prosise, in Microsoft Systems Journal, February 1997 – Vol 12 No 2, article Wicked Code.
If you wanna know about all the magic hidden behind the code, read the article.

Unfortunately, the article is not any much clear about how to make it all work, so I decided to write this.

The whole magic lays in 3 short source files – WaitRsc.h, WaitDlg.h and WaitDlg.cpp.

WaitRsc.h:

#define IDD_WAITDIALOG      500
#define IDC_PROGRESSCTRL    510
#define IDC_MSGCTRL         520

Make sure that these definitions do not collide with any ID’s already defined in resource.h! If they do, simply change the ID’s to any numbers that are not yet used.

WaitDlg.h:

class CWaitDialog : public CDialog
{
protected:
    BOOL* m_pFlag;
public:
    CWaitDialog (BOOL*, LPCTSTR dialogTitle = NULL, LPCTSTR dialogText = NULL);
    virtual ~CWaitDialog ();
    virtual void OnCancel ();
    BOOL Pump();
    void SetPercentComplete(int);
    void SetPercentCompleteAndPump(int);
    void SetMessageText(LPCTSTR);
    void Close();
    bool Cancel() const;    // did user press cancel button?
};

WaitDlg.cpp:

#include
#include
#include "WaitRsc.h"
#include "WaitDlg.h"

CWaitDialog::CWaitDialog (BOOL* pFlag, LPCTSTR pszCaption, LPCTSTR pszText) : CDialog ()
{
    m_pFlag = pFlag;

    // Disable the main window and create the dialog.
    AfxGetMainWnd ()->EnableWindow (FALSE);
    Create (IDD_WAITDIALOG);

    // Initialize the dialog caption and the static text control.
    SetWindowText ((pszCaption == NULL) ? "Working" : pszCaption);

    CStatic* pCtrl = (CStatic*) GetDlgItem (IDC_MSGCTRL);
    pCtrl->SetWindowText ((pszText == NULL) ? "Click Cancel to cancel " \
        "the operation" : pszText); 

    // Display the dialog.
    ShowWindow (SW_SHOW);
}

CWaitDialog::~CWaitDialog ()
{
    Close ();
}

void CWaitDialog::OnCancel ()
{
    *m_pFlag = FALSE;
    Close ();
}

BOOL CWaitDialog::Pump ()
{
    MSG msg;

    // Retrieve and dispatch any waiting messages.
    while (::PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
        if (!AfxGetApp ()->PumpMessage ()) {
            ::PostQuitMessage (0);
            return FALSE;
        }
    }

    // Simulate the framework's idle processing mechanism.
    LONG lIdle = 0;
    while (AfxGetApp ()->OnIdle (lIdle++));
    return TRUE;
}

void CWaitDialog::SetPercentComplete (int nPercent)
{
    if (::IsWindow (m_hWnd)) {
        if (nPercent  100)
            nPercent = 100;

        CProgressCtrl* pCtrl = (CProgressCtrl*) GetDlgItem (IDC_PROGRESSCTRL);
        pCtrl->SetPos (nPercent);
    }
}

void CWaitDialog::SetPercentCompleteAndPump(int nPercent)
{
	SetPercentComplete(nPercent);
	Pump();
}

void CWaitDialog::SetMessageText (LPCTSTR pszText)
{
    if (::IsWindow (m_hWnd)) {
        CStatic* pCtrl = (CStatic*) GetDlgItem (IDC_MSGCTRL);
        pCtrl->SetWindowText (pszText);
    }
}

void CWaitDialog::Close ()
{
    if (::IsWindow (m_hWnd)) {
        AfxGetMainWnd ()->EnableWindow (TRUE);
        DestroyWindow ();
    }
}

bool CWaitDialog::Cancel() const
{
	return (*m_pFlag == FALSE);
}

Simply copy-paste the above source code in the designated files, and add those 3 files to your project.

Next step is to add the resources for the dialog in your program – copy and paste the following block in your MyProject.rc file, under “Dialog” section:

IDD_WAITDIALOG DIALOG DISCARDABLE  0, 0, 186, 96
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
FONT 8, "MS Sans Serif"
BEGIN
    CTEXT           "",IDC_MSGCTRL,8,20,168,12,SS_NOPREFIX
    CONTROL         "",IDC_PROGRESSCTRL,"msctls_progress32",WS_BORDER,8,48,
                    168,16
    PUSHBUTTON      "Cancel",IDCANCEL,72,72,50,14
END

And that’s it! Go to your project, reload the resources.

Usage is as simple as possible:

BOOL bContinue = TRUE;
CWaitDialog dlg (&bContinue, "Please wait...", "Wait for me to do my stuff...");

while (stillWorking && bContinue)
{
    // do your stuff...
    dlg.SetPercentComplete(doneSoFar * 100 / totalToDo);
    dlg.Pump();
}

You can also anytime simply change the text inside of dialog using

dlg.SetMessageText("This is some other text!");

And that’s the whole miracle… and works like a charm! 🙂

Note: if you get any error messages about unknown ID’s, copy-paste those 3 lines from WaitRsc.h to resource.h; that should do the trick.

4 responses to “Simple Add-On Wait Dialog in MFC

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s