Programming a Modeless Property Sheet

Modeless Wizard

Environment: VC6, WIN9x, NT4

Programming a modeless wizard has been a problem that I’ve been trying to
solve for some time now. Since I’ve seen that many others have had difficulty with
this as well, I finally decided to dive into the code and figure out how to do this
once and for all. As it turned out, I was surprised to
see how simple it is to make a Wizard modeless.

To use this trick in your code, simply follow these steps:

  • Insert all the code you would need in your project to create
    a CPropertySheet Wizard. Don’t forget to call the SetWizardMode()
    member function.
  • Override the DoModal() member function. You can use the ClassWizard to do so.
  • Copy the function body from CPropertySheet::DoModal() member function into
    your overridden member function. You will also need to include the
    ‘afxpriv.h’ and ‘afximpl.h’ header files in your source.
  • Eliminate a few lines in the code you have copied
    into your overridden function. Just below, is the code you should have
    inside your function. The lines you should eliminate are marked as comments
    and are shown in bold face.
  • Consider overriding ContinueModal() as well. Take a look at the
    example I prepared. The overridden version of this function makes the wizard
    go away when its parent is closed. If you do not override this member
    function you will get an ASSERT error when the wizard’s parent window
    closes before it does.

NOTE: I haven’t tested this new wizard behavior much, so if you come
up with enhancements please let me know!

Implementation


#include “afxpriv.h”
#include “..\src\afximpl.h”

int CMyPropertySheet::DoModal()
{
ASSERT_VALID(this);
ASSERT(m_hWnd == NULL);

// register common controls
VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);

// finish building PROPSHEETHEADER structure
BuildPropPageArray();
/*
// allow OLE servers to disable themselves
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
pApp->EnableModeless(FALSE);
*/

// find parent HWND
HWND hWndTop;
HWND hWndParent = CWnd::GetSafeOwner_(m_pParentWnd->GetSafeHwnd(), &hWndTop);
AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
psh->hwndParent = hWndParent;
/* BOOL bEnableParent = FALSE;

if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
{
::EnableWindow(hWndParent, FALSE);
bEnableParent = TRUE;
}
*/

HWND hWndCapture = ::GetCapture();
if (hWndCapture != NULL)
::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0);

// setup for modal loop and creation
m_nModalResult = 0;
m_nFlags |= WF_CONTINUEMODAL;

// hook for creation of window
AfxHookWindowCreate(this);
psh->dwFlags |= PSH_MODELESS;
m_nFlags |= WF_CONTINUEMODAL;
HWND hWnd = (HWND)::PropertySheet((PROPSHEETHEADER*)psh);
#ifdef _DEBUG
DWORD dwError = ::GetLastError();
#endif
psh->dwFlags &= ~PSH_MODELESS;
AfxUnhookWindowCreate();

// handle error
if (hWnd == NULL || hWnd == (HWND)-1)
{
TRACE1(“PropertySheet() failed: GetLastError returned %d\n”, dwError);
m_nFlags &= ~WF_CONTINUEMODAL;
}

int nResult = m_nModalResult;
if (ContinueModal())
{
// enter modal loop
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG)
dwFlags |= MLF_NOIDLEMSG;
nResult = RunModalLoop(dwFlags);
}

// hide the window before enabling parent window, etc.
if (m_hWnd != NULL)
{
SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
/* if (bEnableParent)
::EnableWindow(hWndParent, TRUE);
*/
if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
::SetActiveWindow(hWndParent);

// cleanup
DestroyWindow();
/*
// allow OLE servers to enable themselves
if (pApp != NULL)
pApp->EnableModeless(TRUE);
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
*/

return nResult;
}

Downloads

Download sample source – 43 KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read