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.


Comments

  • You a saviour! Works perfectly!

    Posted by uNsignedINT on 12/07/2005 05:47am

    Hey! I have been looking for solutions to close a modeless dialog by clicking out side the dialog and with these functions I have solved the problem!! To close my dialog i've just implemented OnNcActivate and called EndDialog(). Thanks!!! :)

    Reply
  • Fix for bug with OnIdle()

    Posted by RobertWebb on 03/15/2005 02:08am

    This is a great little article. I noticed that OnIdle() didn't keep getting called though always. I don't really understand why exactly, but adding these lines:

    	nIdle = TRUE;
    	nIdleCount = 0;
    
    after the final "} while (::PeekMessage..." line fixed it. All the "normal" messages have been processed at that time, so it makes sense to me to return to idle processing. Thanks, Rob.

    Reply
  • Brilliant!! Works great. Just one recommendation.

    Posted by Axter on 01/27/2005 12:03pm

    I recommend you add a default value for the constructor, just as most windows class have default NULL values. Example: NonModalDialog( CWnd *pParent = NULL) : ModalDialog( pParent ) { } Otherwise, this class is PERFECT. I have to add it to my toolbox!!! Thanks!

    Reply
  • How to get WM_KICKIDLE message back in the dialog messages?

    Posted by Legacy on 09/10/2003 12:00am

    Originally posted by: Daniel

    When I use the NonModalDialog I can't use the mapped function of the message WM_KICKIDLE.

    How to solve this?

    Reply
  • how do i create a child dialog that will be displayed on the task bar???

    Posted by Legacy on 03/27/2003 12:00am

    Originally posted by: sriram


    your site is very userful to us..

    Reply
  • how do i create a child dialog that will be displayed on the task bar???

    Posted by Legacy on 03/27/2003 12:00am

    Originally posted by: sriram


    your site is very userful to us..

    Reply
  • How do I create a modeless dialog which can be destroyed by clicking any key down?

    Posted by Legacy on 04/23/2001 12:00am

    Originally posted by: ydong

    How do I create a modeless dialog which can be destroyed by clicking any key down (character keys, system keys and mouse keys), and the dialog without OK, Cancel and Close
    button?

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

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds