[From CodeGuru: http://www.codeguru.com/cpp/w-d/dislog/ddxddv/article.php/c1997/]
Enhacing CDialogBar with InitDialog and DDX
Rating:
| Alger Pike (view profile) August 7, 1998 |
As you all know, the CDialogBar control, is the MFC control that lets the programmer add any control he wants, into a dockable window via a dialog template. This class is very much like the CDialog class iteself; however, there are two major differences between the classes.
CDialogBar does not have an OnInitDialog function where you can initialize your controls, nor does it support the DDX mechanism. These are two simple items which can be added to a new class, thereby making it possible to have a CDialogBar that behaves much more like a regular CDialog.
As it turns out, a class that does this is quite simple to implement. The support for DDX is already built into CWnd, the base class of CDialogBar. The required DDX_??? Functions to update the controls are global MFC functions and CWnd already supports the DDX mechanism. Why DDX support never found itself into the MFC CDialogBar is really puzzling? To add it all you need to do is call the already provided functions at the proper time. By taking these few simple steps, you then create a powerful, yet simple class that really makes getting the data from your CDialogBar controls much eaiser. Below I will outline the steps to creating the CInitDialogBar class.
The first step is to look at where CDialog initializes the DDX mechanism. It does so in its OnInitDialog function, with its first call to UpdateData(). This function is a CWnd function, not a CDialog function. Since CDialogBar has no OnInitDialog() function, we need to mimic this behavior with our derived CDialogBar class. In CDialog, OnInitDialog() gets called in response to the dialog window sending itself a WM_INITDIALOG message. CDialogBar has no such message since its really a CWnd and not strictly a dialog window. The important thing to note, is that the WM_INITDIALOG message, in a real dialog, is sent to the window after its handle exists. This means that although we won’t officially send ourselves a message in the CInitDialogBar class, that we can call our own version of OnInitDialog() after the handle to our window exists. We modify the create function of CDialogBar to do this. Since Create() for a CDialogBar is overloaded we redefine each create and make out modifications to it.
First in the CInitDialogBar class redefine the public create functions:
BOOL Create(CWnd * pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID);By doing this, we don’t change the way our CInitDialogBar is created.
BOOL Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID);
Next, define a virtual function, which all derived classes will override. This function, will provide the missing OnInitDialog() functionality. Also we re-declare the virtual CWnd DDX funtion DoDataExchange as well:
virtual BOOL OnInitDialogBar();We are now ready to implement our new functions. Let’s start with the Create(). CDialogBar already has a Create() which will create the control for us. Let’s not reinvent the wheel, but instead reuse it. (Both overloaded functions are changed in the same way so I will only show one here.) After we call the base class, Create() function our m_hWnd is valid. This is what we have been waiting for. We can now call our OnInitDialogBar() function.
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CInitDialogBar)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Let MFC Create the controlJust like in CDialog the OnInitDialogBar() will initialize the DDX mechanism for us by calling The UpdateData() function. For this reason it will be important for derived classes to call the base class. The UpdateData() function will call our virtual DoDataExchange(), for now we will leave this function blank. We will put our initializing code in our derived classes. That’s pretty much all the new code we need. We are now ready to start using our class and deriving our own objects from it.
// Call the base class Create()
if(!CDialogBar::Create(pParentWnd,
lpszTemplateName, /*(nIDTemplate) for other Create()*
nStyle, nID))
{
return FALSE;
}
// Since there is no WM_INITDIALOG message we have to call
// our own InitDialog function ourselves after m_hWnd is valid
if(!OnInitDialogBar())
return FALSE;
return TRUE;
The first step is to derive your own object from CInitDialogBar. To create the Dialog bar in our CMainFrame object do the following in the CMainFrame::OnCreate():
// CInitDialogBar m_wndDialogBar; in mainframe.hAt this point, a dialog bar should appear with the template that you provide for it. The controls don’t communicate with the CInitDialogBar derived object; let’s remedy this. Because of the way CInitDialogBar was written we now only need to override our two virtual functions. Below are the functions and the required code to initialize a CBitmapButton and CheckBox.
if (!m_wndDialogBar.Create(this, IDD_DBAR, CBRS_LEFT, 0))
{
TRACE0("Failed to create InitDialogBar\n");
return -1; // fail to create
}
m_wndDialogBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndDialogBar);
BOOL CMainDialogBar::OnInitDialogBar()We now have a CDialogBar that uses the DDX mechanism. This makes it much easier to keep out controls and their variables synchronized. Any time you want the current state of your DDX controls just call UpdateData(TRUE). This will synchronize the variables with the controls current state. And likewise to set the controls to a know state call UpdateData(FALSE).
{
// Support for DDX mechanism
// If you do not want DDX then
// do not call base class
// All out DDX controls are intialized
// the virtual call to DoDataExchange.
CInitDialogBar::OnInitDialogBar();
// Initialize any controls NOT supported by DDX
// CBitmapButton is one
m_OKButton.AutoLoad(IDOK, this);
return TRUE;
}
void CMainDialogBar::DoDataExchange(CDataExchange* pDX)
{
ASSERT(pDX);
CInitDialogBar::DoDataExchange(pDX);
// DDX_??? functions to synchronize control with
// data or control object
//{{AFX_DATA_MAP(CAboutDlg)
DDX_Check(pDX, IDC_CHECK1, m_CheckBox);
//}}AFX_DATA_MAP
}
The complete code for CInitDialogBar and a sample derived object follow.
Enjoy! :)
//////////////////////////////////////////////////////////////////////Sample derived Control:
//
// InitDialogBar.h: interface for the CInitDialogBar class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_INITDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_)
#define AFX_INITDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
////////////////////////////////////////////////////////////////////////////
//
// CInitDialogBar window
//
////////////////////////////////////////////////////////////////////////////
class CInitDialogBar : public CDialogBar
{
DECLARE_DYNAMIC(CInitDialogBar)
// Construction / Destruction
public:
CInitDialogBar();
virtual ~CInitDialogBar();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CInitDialogBar)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
public:
BOOL Create(CWnd * pParentWnd, UINT nIDTemplate, UINT nStyle, UINT
nID);
BOOL Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT
nStyle, UINT nID);
protected:
virtual BOOL OnInitDialogBar();
// Generated message map functions
protected:
//{{AFX_MSG(CInitDialogBar)
// NOTE - the ClassWizard will add and remove member functions
here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
////////////////////////////////////////////////////////////////////////////
/
#endif // !defined(AFX_INITDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_)
////////////////////////////////////////////////////////////////////////////
//
// InitDialogBar.cpp: implementation of the CInitDialogBar class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "spectra.h"
#include "InitDialogBar.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNAMIC(CInitDialogBar, CDialogBar)
BEGIN_MESSAGE_MAP(CInitDialogBar, CDialogBar)
//{{AFX_MSG_MAP(CInitDialogBar)
// NOTE - the ClassWizard will add and remove mapping macros
here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CInitDialogBar::CInitDialogBar()
{
// In derived classes set intial
// state of control(s) here
}
CInitDialogBar::~CInitDialogBar()
{
}
BOOL CInitDialogBar::Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName,
UINT nStyle, UINT nID)
{
// Let MFC Create the control
if(!CDialogBar::Create(pParentWnd, lpszTemplateName, nStyle, nID))
return FALSE;
// Since there is no WM_INITDIALOG message we have to call
// our own InitDialog function ourselves after m_hWnd is valid
if(!OnInitDialogBar())
return FALSE;
return TRUE;
}
BOOL CInitDialogBar::Create(CWnd * pParentWnd, UINT nIDTemplate,
UINT nStyle, UINT nID)
{
if(!Create(pParentWnd, MAKEINTRESOURCE(nIDTemplate), nStyle, nID))
return FALSE;
// Since there is no WM_INITDIALOG message we have to call
// our own InitDialog function ourselves after m_hWnd is valid
if(!OnInitDialogBar())
return FALSE;
return TRUE;
}
BOOL CInitDialogBar::OnInitDialogBar()
{
// Support for the MFC DDX model
// If you do not want this do not call the base class
// from derived classes
UpdateData(FALSE);
return TRUE;
}
void CInitDialogBar::DoDataExchange(CDataExchange* pDX)
{
//Derived Classes Overide this function
ASSERT(pDX);
CDialogBar::DoDataExchange(pDX);
// In derived class call the DDX_??? functions to set/retrieve values
// of your controls. See example derived class for how to do this.
}
//////////////////////////////////////////////////////////////////////
//
// MainDialogBar.h: interface for the CMainDialogBar class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_MAINDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_)
#define AFX_MAINDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#include "InitDialogBar.h"
// CMainDialogBar window
class CMainDialogBar : public CInitDialogBar
{
DECLARE_DYNAMIC(CMainDialogBar)
// Construction
public:
CMainDialogBar();
virtual ~CMainDialogBar();
// Attributes
public:
protected:
// Control Objects
CBitmapButton m_OKButton;
// Control Variables
BOOL m_CheckBox;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMainDialogBar)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
protected:
virtual BOOL OnInitDialogBar();
// Implementation
public:
// Generated message map functions
protected:
//{{AFX_MSG(CMainDialogBar)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
////////////////////////////////////////////////////////////////////////////
/
#endif // !defined(AFX_MAINDIALOGBAR_H__46B4D2B3_C982_11D1_8902_0060979C2EFD__INCLUDED_)
////////////////////////////////////////////////////////////////////////////
//
// MainDialogBar.cpp: implementation of the CMainDialogBar class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "spectra.h"
#include "MainDialogBar.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNAMIC(CMainDialogBar, CInitDialogBar)
BEGIN_MESSAGE_MAP(CMainDialogBar, CInitDialogBar)
//{{AFX_MSG_MAP(CMainDialogBar)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CMainDialogBar::CMainDialogBar()
{
//Set Initial conditions for controls
m_CheckBox = 1;
}
CMainDialogBar::~CMainDialogBar()
{
}
BOOL CMainDialogBar::OnInitDialogBar()
{
// Support for DDX mechanism
// If you do not want DDX then
// do not call base class
CInitDialogBar::OnInitDialogBar();
// Update any controls NOT supported by DDX
// CBitmapButton is one
m_OKButtton.AutoLoad(IDOK, this);
return TRUE;
}
void CMainDialogBar::DoDataExchange(CDataExchange* pDX)
{
ASSERT(pDX);
CInitDialogBar::DoDataExchange(pDX);
// DDX_??? functions to associate control with
// data or control object
// Call UpdateData(TRUE) to get data at any time
// Call UpdateData(FALSE) to set data at any time
//{{AFX_DATA_MAP(CAboutDlg)
DDX_Check(pDX, IDC_CHECK1, m_CheckBox);
//}}AFX_DATA_MAP
}