Enhacing CDialogBar with InitDialog and DDX

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);

BOOL Create(CWnd * pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID);
By doing this, we don’t change the way our CInitDialogBar is created.

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();

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CInitDialogBar)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
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.
// Let MFC Create the control
// 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;
Just 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.
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.h

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);
At 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.
BOOL CMainDialogBar::OnInitDialogBar()
{
	// 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
}
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).

The complete code for CInitDialogBar and a sample derived object follow.

Enjoy! :)
//////////////////////////////////////////////////////////////////////
//
// 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.
}

Sample derived Control:
//////////////////////////////////////////////////////////////////////
//
// 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
}

Last updated: 11 May 1998