dcsimg
 

Make Modal Dialogs Behave Like Modeless Dialogs

Monday Jan 29th 2001 by Venkata Sundaram

Simulates modeless dialog behaviour while modal dialog-like programmatic control

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.
Home
Mobile Site | Full Site
Copyright 2018 © QuinStreet Inc. All Rights Reserved