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.
- Include the NewButton.h and NewButton.cpp files in your project
- Create a dialog with a button with Owner Draw style
- Define the class that manage the dialog (must derive from CDialog or any other class that derive from CDialog), in the example CDlg
- 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
//{{AFX_DATA(CDlg)
enum { IDD = IDD_PROPERTIES };
CNewButton m_KeepVisible;
//}}AFX_DATA - 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 )
{
//{{AFX_DATA_INIT(CDlg)
//}}AFX_DATA_INIT
}
- in the .h file
- Define in Dlg.h public section the function:
BOOL Create(); // Creation of mode-less dialog
- 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:
static LRESULT CALLBACK NewWndProc( HWND, UINT, WPARAM, LPARAM );
// New window procedure of the dialog - With Class Wizard re-define WM_INITDIALOG, WM_CLOSE and PreTraslateMessage(), and define the button click message function; in the example:
OnKeepVisible()
- Define the UserMessage to be used by the dialog to communicate with its father:
#define WM_CLOSEDLG WM_USER+5
- 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()
{
CDialog::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
UINT nBitmapIDs[] = { IDB_VISIBLEU, IDB_VISIBLED,
IDB_VISIBLEU_OVER, IDB_VISIBLED_OVER };
m_KeepVisible.SetParent( this, nBitmapIDs );
// Change of the default window procedure
WNDPROC temp = reinterpret_cast(::SetWindowLong( // return TRUE unless you set the focus to a control
GetSafeHwnd(), GWL_WNDPROC, reinterpret_cast(NewWndProc) ));
if ( !m_MFCWndProc ) m_MFCWndProc = temp;
m_pDialog = this;
return TRUE;
// 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
m_KeepVisible.SetMode();
// 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 );
 CDialog::OnClose();
}
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;
break;
case VK_ESCAPE:
m_pDialog->m_pParent->PostMessage( WM_CLOSEDLG );
return true;
}
// Tooltip management
m_toolTip.RelayEvent( pMsg );
return CDialog::PreTranslateMessage(pMsg);
} - 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
protected:
//{{AFX_MSG(CMainFrame)
...
//}}AFX_MSG
afx_msg LRESULT OnCloseDlg( WPARAM wParam, LPARAM lParam );
DECLARE_MESSAGE_MAP()while in the .CPP file the message map:
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
...
//}}AFX_MSG_MAP
ON_MESSAGE( WM_CLOSEDLG, OnCloseDlg )
END_MESSAGE_MAP()and the function implementation:
LRESULT CMainFrame::OnCloseDlg( WPARAM wParam, LPARAM lParam )
{
if ( m_pDlg )
{
m_pDlg->DestroyWindow();
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->SetFocus();
else
{
m_pDlg = new CDlg( this );
m_pDlg->Create();
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 )
{
m_pDlg->DestroyWindow();
delete m_pDlg;
}
Thanks to Matteo Benzoni and Diego Perrotta
Downloads
Download demo project – 35 Kb
Download source – 5 Kb