Make Modal Dialogs Behave Like Modeless Dialogs

Environment: Visual C++ 4.0 (and above)

Description

This template class simulates the behaviour of a modeless dialog while allowing some
of the programmatic control of a modal dialog. When modal dialog are closed control is
immediately returned to the caller of the DoModal member function:

CMyDialog dlg;

if( dlg.DoModal() ) { ... }

However, the same can not be said of modal dialogs where the Create/ShowWindow member
function pair need to be used in order to display the dialog.

CMyDialog dlg;
dlg.Create( IDD_MY_DIALOG );
dlg.ShowWindow();

...

if( you receive some message that the non-modal has closed )
{
...
}

The above code segment creates and activates a non-modal dialog. How and when you get
control back generally involves some rather tricky programming – in other words, it’s not
nearly as straight-forward as using a modal dialog.

Usage

NonModalDialog<MyDlg>myDlg( AfxGetMainWnd() );
myDlg.DoModal();

...

class CWnd;

template <class ModalDialog>class NonModalDialog
: public ModalDialog
{
public :
NonModalDialog( CWnd *pParent )
: ModalDialog( pParent ) { }

virtual int DoModal();
virtual void EndModalLoop( int );
protected:
int m_nStillActive;
};

template <class ModalDialog>
inline int NonModalDialog<ModalDialog>::DoModal()
{
MSG l_objMessage;
CWinThread* l_pThread = AfxGetThread();

ASSERT_VALID( l_pThread );

Create( ModalDialog::IDD );
ShowWindow( SW_SHOW );
m_nStillActive = 1;

//--------- Thanks to Microsoft. This block
//--------- is from CWinThread::Run
// from Thrdcore.cpp, with appropriate changes.
ASSERT_VALID(this);
// for tracking the idle time state
BOOL l_nIdle = TRUE;
LONG l_nIdleCount = 0;

// acquire and dispatch messages until
// a WM_QUIT message is received,
// or the dialog box is closed.
for (;;)
{
// phase1: check to see if we can do idle work
while( l_nIdle && !::PeekMessage( &l_objMessage,
NULL,
NULL,
NULL,
PM_NOREMOVE ) )
{
// call OnIdle while in bIdle state
if( !l_pThread->OnIdle( l_nIdleCount++ ) )
l_nIdle = FALSE; // assume "no idle" state
}

// phase2: pump messages while available
do
{
// Relent control back to Windows if the dialog
// box has closed.
if( !m_nStillActive )
return m_nModalResult;

if( l_objMessage.message == WM_SYSCOMMAND
&& l_objMessage.wParam == SC_CLOSE )
OnCancel(); // Close the dialog box first.

// pump message, but quit on WM_QUIT
if( !l_pThread->PumpMessage() )
return l_pThread->ExitInstance();

// reset "no idle" state after pumping
// "normal" message
if( l_pThread->IsIdleMessage( &l_objMessage ) )
l_nIdle = TRUE, l_nIdleCount = 0;
} while( ::PeekMessage( &l_objMessage,
NULL,
NULL,
NULL,
PM_NOREMOVE ) );
}
ASSERT(FALSE); // not reachable
//---------------------
}

template <class ModalDialog>inline void
NonModalDialog<ModalDialog>::EndModalLoop(int p_nResult)
{
m_nStillActive = 0;
ModalDialog::EndModalLoop( p_nResult );
}

Limitations

  1. Applies only to MFC versions that properly support OCX containment. ( v4.0 and later )
  2. From inside the dialog, you close it using EndDialog( ... ) only.
  3. You MUST take only two parameters, one is the IDD of your dialog, and the next is CWnd*, in your dialog's constructor.

More by Author

Must Read