Programming a Modeless Property Sheet
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" int CMyPropertySheet::DoModal()
// register common controls
// finish building PROPSHEETHEADER structure
if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
// setup for modal loop and creation
// hook for creation of window
// handle error
int nResult = m_nModalResult;
// hide the window before enabling parent window, etc.
// cleanup
#include "..\src\afximpl.h"
{
ASSERT_VALID(this);
ASSERT(m_hWnd == NULL);
VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
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;
{
::EnableWindow(hWndParent, FALSE);
bEnableParent = TRUE;
}
*/
HWND hWndCapture = ::GetCapture();
if (hWndCapture != NULL)
::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0);
m_nModalResult = 0;
m_nFlags |= WF_CONTINUEMODAL;
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();
if (hWnd == NULL || hWnd == (HWND)-1)
{
TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError);
m_nFlags &= ~WF_CONTINUEMODAL;
}
if (ContinueModal())
{
// enter modal loop
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG)
dwFlags |= MLF_NOIDLEMSG;
nResult = RunModalLoop(dwFlags);
}
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);
DestroyWindow();
/*
// allow OLE servers to enable themselves
if (pApp != NULL)
pApp->EnableModeless(TRUE);
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
*/
return nResult;
}

Comments
Modeless Property Sheet
Posted by Rita1981 on 05/18/2006 05:49pmThe code above seems really complicated and I could not get it to compile. Does anyone know an easy way to create a modeless property sheet? My goal is just to have a simple Modeless property sheet that has NO Ok, Apply , Cancel and help buttons. I managed to get rid of the buttons on a Modal Property sheet but the empty space still appears underneath the tabs. Please help. Thanks,
ReplyModeless Property Sheet
Posted by Rita1981 on 05/18/2006 05:36pmThe code above seems really complicated and I could not get it to compile. Does anyone know an easy way to create a modeless property sheet? My goal is just to have a simple Modeless property sheet that has NO Ok, Apply , Cancel and help buttons. I managed to get rid of the buttons on a Modal Property sheet but the empty space still appears underneath the tabs. Please help. Thanks,
ReplyHow can i position the modeless porpertysheet dlg
Posted by Legacy on 10/16/2003 12:00amOriginally posted by: fting
CConcreteLxlPropertySheet m_ConcreteLxlPropertySheet(""); CConcreteLxlPage1 page1; CConcreteLxlPage2 page2; m_ConcreteLxlPropertySheet.AddPage(&page1); m_ConcreteLxlPropertySheet.AddPage(&page2); m_ConcreteLxlPropertySheet.DoModal(); m_ConcreteLxlPropertySheet.MoveWindow(myRect.left+myRect.Width()/9,myRect.top+32,500,400,true);
However, it seems that this doesn't work ,besides, when i close the propertysheet dlg, I am warned-"debug assertion failed".
ReplyI don't know how to deal with it.
Could someone give me some help??
Thank you very much!!!!!
Create Modeless PropertySheet : In MS knowledge Base
Posted by Legacy on 04/07/2003 12:00amOriginally posted by: duckbill
You can find an article which is explaining the method in Microsoft Knowledge Base.
Go to http://msdn.microsoft.com/, and
search with keyword Q146916 in Knowledge Base Category.
Good luck.
ReplyClosing of modeless Dialog Box
Posted by Legacy on 08/03/2000 12:00amOriginally posted by: Hariprasad Rao
ReplyHow can i close modeless propertysheet?
Posted by Legacy on 05/06/2000 12:00amOriginally posted by: Seorak, Lee
Please help me. I can't close this dialog.
I used to Create Function
ReplyASSERT failure!!!!!!!!!!!!!!!!in RunModal(), please help
Posted by Legacy on 03/15/2000 12:00amOriginally posted by: bhaskar
Assert failure in CWnd::DestroyWindow
#ifdef _DEBUG
ASSERT(m_hWnd == hWndOrig);
which is in
CWnd::FilterToolTipMessage()
pToolTip->DestroyWindow();
delete pToolTip;
which is in
void PASCAL CWnd::_FilterToolTipMessage(MSG* pMsg, CWnd* pWnd)
{
pWnd->FilterToolTipMessage(pMsg);
}
which is in
BOOL CWnd::PreTranslateMessage(MSG* pMsg)
{
// handle tooltip messages (some messages cancel, some may cause it to popup)
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
if (pModuleState->m_pfnFilterToolTipMessage != NULL)
(*pModuleState->m_pfnFilterToolTipMessage)(pMsg, this);
// no default processing
return FALSE;
}
etc..
Can you spot the mistake???
Bhaskar
Reply2 modeless PropertySheet : don't close the first!!!
Posted by Legacy on 01/19/2000 12:00amOriginally posted by: deshays
2 modeless PropertySheet : don't close the first!!!
ReplyWhere's the problem?
Unhandled exception for Modeless Property Sheet (not Wizard)
Posted by Legacy on 10/13/1999 12:00amOriginally posted by: Edmond Chui
By using the same trick as in the example, I turned a modal property sheet to modeless. Everything worked fine until I tried to kill the parent window before killing the property sheet. I got an Unhanled exception error (Access Violation) when the if(!ContinueModal()) line is called inside RunModalLoop(), which in turn is called by DoModeless(). Any suggestion on what's wrong?
Also, how to bring the modeless property sheet behind the parent when it yields the input focus?
Thanks,
ReplyEdmond
Where is the focus on a modeless property sheet after clicking on the HELP button on the sheet
Posted by Legacy on 08/03/1999 12:00amOriginally posted by: Terry Chan
I try to make the HELP button and the F1 on my modeless property sheet work, but they have been acting so weird . For instance, if F1 is clicked, then F1 is not active, but if I lick the cursor somewhere in side the page, F1 works. It seems to me that after HeLP button is released, the focus on this sheet is gone, Please give me some idea how to solve this problem
Thanks
Terry
ReplyLoading, Please Wait ...