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


Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Microsoft® Office 365 is a top choice for enterprises that want a cloud-based suite of productivity/ collaboration applications. With Office 365, you get access to Microsoft™ Office solutions practically anytime, anywhere, on virtually any device. It's a great option for current Microsoft users who can now build on their experience with Microsoft™ solutions while enjoying the flexibility of a cloud-based delivery. But even organizations with no previous investment in Microsoft will find that …

  • Live Event Date: July 28, 2016 @ 1:00 p.m. ET / 10:00 a.m. PT Jepsen tests are third-party tests for distributed databases that validate vendors' guarantees about how they perform under various failure scenarios, especially network partitions. These have proven their value as tools in any distributed system tester's arsenal. When the creator of Jepsen, Kyle Kingsbury, started his Jepsen-for-Hire business last fall, VoltDB immediately got in line, and over the past two months, our solution was given the most …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date