DevStudio-like Properties Dialog

Environment: VC6 SP3, NT4 SP5

These are the steps to follow to create a dialog resembling the DevStudio Properties dialog: clicking outside the dialog induces dialog closing, unless the Keep Visible button was previously pressed.

  1. Include the NewButton.h and NewButton.cpp files in your project
  2. Create a dialog with a button with Owner Draw style
  3. Define the class that manage the dialog (must derive from CDialog or any other class that derive from CDialog), in the example CDlg
  4. Add a member variable for the button, must be of CNewButton class; if the Class Wizard doesn’t permit to choose that variable’s type, you could choose the CButton class and then modify it in the .h file of the CDlg class (in the example Dlg.h); remember to insert the line

    #include "NewButton.h"

    before the class definition:

    // Dialog Data

    enum { IDD = IDD_PROPERTIES };
    CNewButton m_KeepVisible;


  5. Modify CDlg constructor removing the default value of the pParent parameter; it is essential for the dialog to know its father:
    • in the .h file

      CDlg(CWnd* pParent);

    • in the .CPP file

      CDlg::CDlg(CWnd* pParent) : CDialog(CDlg::IDD, pParent), m_pParent( pParent )



  6. Define in Dlg.h public section the function:

    BOOL Create(); // Creation of mode-less dialog

  7. Define in Dlg.h protected section the following member variables:

    CWnd* m_pParent; // Parent of the dialog
    CToolTipCtrl m_toolTip; // Tooltip management
    static WNDPROC m_MFCWndProc; // Pointer to the default MFC window procedure
    static CDlg* m_pDialog; // Pointer to the active instance of the dialog (this variable is correct if we do the hypothesis that only one instance of CDlg could be active at once; otherwise is necessary to define a map which related a HWND with a CDlg*)

    and the function:

    // New window procedure of the dialog

  8. With Class Wizard re-define WM_INITDIALOG, WM_CLOSE and PreTraslateMessage(), and define the button click message function; in the example:


  9. Define the UserMessage to be used by the dialog to communicate with its father:

    #define WM_CLOSEDLG WM_USER+5

  10. In the .CPP file define the static variables:

    WNDPROC CDlg::m_MFCWndProc = 0;
    CDlg* CDlg::m_pDialog = NULL;

    and insert the following implementation of the previously declared functions and message handlers:

    BOOL CDlg::OnInitDialog()
    // Creation and init of tooltip for the Keep Visible button
    m_toolTip.Create( this, TTS_ALWAYSTIP );
      m_toolTip.Activate( TRUE );
      m_toolTip.AddTool( GetDlgItem( IDC_KEEP_VISIBLE ), "Keep Visible" );
    // Init of the Keep Visible button: set the parent of the button (dialog
      // that contains it) and the bitmaps which shows when the button is
      // pressed/released and the mouse is over/non-over
      m_KeepVisible.SetParent( this, nBitmapIDs );
    // Change of the default window procedure
    WNDPROC temp = reinterpret_cast(::SetWindowLong(
       GetSafeHwnd(), GWL_WNDPROC, reinterpret_cast(NewWndProc) ));
      if ( !m_MFCWndProc ) m_MFCWndProc = temp;
      m_pDialog = this;
      return TRUE;
    // return TRUE unless you set the focus to a control
       // EXCEPTION: OCX Property Pages should return FALSE
    BOOL CDlg::Create()
      return CDialog::Create( CDlg::IDD );
    void CDlg::OnKeepVisible()
      CPoint point;
      ::GetCursorPos( &point );
      CRect rect;
      m_KeepVisible.GetWindowRect( &rect );
    // Check that the mouse is over the button
    if ( rect.PtInRect( point ) )
    // Change the button state
    // The SetState function must be called two times
    m_KeepVisible.SetState( m_KeepVisible.GetMode() );
        m_KeepVisible.SetState( m_KeepVisible.GetMode() );
    LRESULT CALLBACK CDlg::NewWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    // The new window procedure manages only the dialog inactivate;
      // the other msgs are managed by default window procedure
    if ( message == WM_ACTIVATE )
        if ( wParam == 0 )
          if ( !m_pDialog->m_KeepVisible.GetMode() )
            m_pDialog->m_pParent->PostMessage( WM_CLOSEDLG );
          return FALSE;

      return ::CallWindowProc( m_MFCWndProc, hWnd, message, wParam, lParam );
    void CDlg::OnClose()
    // Parent notify
    m_pDialog->m_pParent->PostMessage( WM_CLOSEDLG );
    BOOL CDlg::PreTranslateMessage(MSG* pMsg)
    // Different management of the WM_KEYDOWN msg when is pressed the ENTER button
      // (the default management closes the dialog) or the ESC (we want to close the
      // dialog)
    if ( pMsg->message == WM_KEYDOWN )
        switch ( (int)pMsg->wParam )
          case VK_RETURN:
            if ( m_KeepVisible.GetMode() )
              return true;
          case VK_ESCAPE:
            m_pDialog->m_pParent->PostMessage( WM_CLOSEDLG );
            return true;
    // Tooltip management
    m_toolTip.RelayEvent( pMsg );
      return CDialog::PreTranslateMessage(pMsg);

  11. At this time we need to manage the dialog from its father (in the example the father is the MainFrame), in particular the closing message; so in the .h file we need to insert the definition of the message handler:

    // Generated message map functions
    afx_msg LRESULT OnCloseDlg( WPARAM wParam, LPARAM lParam );

    while in the .CPP file the message map:


    and the function implementation:

    LRESULT CMainFrame::OnCloseDlg( WPARAM wParam, LPARAM lParam )
      if ( m_pDlg )
         delete m_pDlg;
         m_pDlg = NULL;
      return 0L;

    where m_pDlg is a pointer to the dialog active instance.

To correctly use this source it’s necessary define, in the MainFrame or where you want manage the dialog, a member variable which points to the dialog active instance:

CDlg* m_pDlg;

furthermore, in the code, where you want create the dialog, insert the next lines:

// If the dialog already exists set the focus to it, otherwise create the dialog and show it
if ( m_pDlg )
  m_pDlg = new CDlg( this );
  m_pDlg->ShowWindow( TRUE );

Don’t forget to initialize the pointer in the constructor:

m_pDlg = NULL;

and verify, in the destructor, that there isn’t active instance:

if ( m_pDlg )
  delete m_pDlg;

Thanks to Matteo Benzoni and Diego Perrotta


Download demo project – 35 Kb
Download source – 5 Kb

More by Author

Must Read