- Download demo project - 21.65 KB
Introduction
The MFC framework provides an efficient mechanism for transferring and validating data in a dialog box through the DDX and DDV routines. Dialog Data Exchange (DDX) is an easy way to initialize the controls in a dialog box and gather data input by the user. Dialog Data Validation (DDV) is an easy way to validate data entry in a dialog box.
The framework calls CWnd::DoDataExchange
to exchange and validate dialog data. When a class is derived from CDialog
, you need to override this member function if you wish to utilize the framework's automatic data exchange and validation. An implementation of DoDataExchange
is shown below:
C++
void CDataExchangeDlg::DoDataExchange(CDataExchange* pDX){ //should precede the DDX and DDV routines CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_strEdit); DDX_Check(pDX, IDC_CHECK1, m_bCheck);}
It's to be noted that DoDataExchange
is never called directly, it's called by the CWnd::UpdateData
member function.
C++
BOOL UpdateData( BOOL bSaveAndValidate = TRUE );
UpdateData
is called to initialize data in a dialog box, or to retrieve and validate dialog data. The bSaveAndValidate
flag indicates whether a dialog box is being initialized (set bSaveAndValidate
= FALSE
) or data is being retrieved (set bSaveAndValidate
= TRUE
).
C++
//From wincore.cppBOOL CWnd::UpdateData(BOOL bSaveAndValidate){ BOOL bOK = FALSE; // assume failure TRY { DoDataExchange(&dx); bOK = TRUE; // it worked } CATCH(CUserException, e) { // validation failed - user already alerted, fall through ASSERT(!bOK); } return bOK;}
We see from the above that UpdateData()
exists solely to create the data exchange object and to catch any exceptions and convert them into a boolean return value (where false
indicates a failure in the exchange/validation process).
Let's have a look at the CDataExchange
class declared in the afxwin.h, as well:
C++
//CDataExchange - for data exchange and validationclass CDataExchange{public: // Attributes BOOL m_bSaveAndValidate; // TRUE => save and validate data CWnd* m_pDlgWnd; // container usually a dialog // Operations (for implementors of DDX and DDV procs) HWND PrepareCtrl(int nIDC); HWND PrepareEditCtrl(int nIDC); void Fail(); // will throw exception CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate); // Implementation UINT m_idLastControl; // last control used (for validation) BOOL m_bEditLastControl; // last control was an edit item};
The m_bSaveAndValidate
flag is the same as described above. Some other interesting members of the CDataExchange
class are:
m_pDlgWnd
: The window (usually a dialog) that contains the controls.PrepareCtrl
andPrepareEditCtrl
: Prepares a dialog control for data exchange. Stores that control's handle for setting the focus if validation fails.PrepareCtrl
is used for non-edit controls, andPrepareEditCtrl
is used for edit controls. ThePrepareEditCtrl
first callsPrepareCtrl
, and then setsm_bEditLastControl
toTRUE
. The reason to check whether the last control was an edit control is that the focus to the edit control is set by sending theEM_SETSEL
message.Fail
: Called after bringing up a message box alerting the user to the input error. This routine will restore the focus to the last control (the last call toPrepareCtrl
/PrepareEditCtrl
) and throw an exception.
Dialog Data Exchange
MFC provides a library of transfer functions grouped under the heading of Dialog Data Exchange (DDX). One DDX function is defined for each type of control and each reasonable variable type. For example, the function to exchange data between an edit control and an integer variable is:
void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value);
The CDataExchange
parameter is used to hold context information during the exchange process. The ctrlID
parameter holds the ID of an edit control, and value
is a reference to a data member in the dialog class.
MFC overloads the DDX functions to handle parsing for different program variable types. For example, the DDX_Text()
function has the following variations:
C++
void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, BYTE& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, short& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LONGLONG& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, ULONGLONG& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LPTSTR value, int nMaxLen);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, float& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, double& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, COleCurrency& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, COleDateTime& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, GUID& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DECIMAL& value);void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, FILETIME& value);
MFC provides different DDX functions for different types of controls. For example, DDX_Check()
works with check boxes, DDX_LBString()
works with list boxes, etc. Look into afxdd_.h for all the available exchange and validation routines.
We can easily extend the DDX mechanism to work with new types. But before we do that, let's have a look at an implementation of DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
to understand how these routines work.
C++
//From dlgdata.cppvoid AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value){ HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC); if (pDX->m_bSaveAndValidate) { int nLen = ::GetWindowTextLength(hWndCtrl); ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1); value.ReleaseBuffer(); } else { AfxSetWindowText(hWndCtrl, value); //This is just a string helper function. //It checks whether the text of the edit control //really changed, if yes then only it updates //its value, otherwise not (this helps reduce //the flash in the controls). }}
The code is simple and self-explanatory. Following that, we can set our own custom DDX routines. The following checks whether the user entered something in the edit control or not:
C++
void DDX_TextNotEmpty (CDataExchange* pDX, int nIDC, CString& value){ HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC); if (pDX->m_bSaveAndValidate) { int nLen = ::GetWindowTextLength(hWndCtrl); ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1); value.ReleaseBuffer(); if (value.IsEmpty ()) { AfxMessageBox (_T("Enter something in the text field."), MB_ICONSTOP); pDX->Fail (); //Fail () sets the focus on to the edit control //and throws an exception } } else { ::SetWindowText(hWndCtrl, value); }}
Dialog Data Validation
In addition to providing transfer capabilities with dialog boxes, MFC also provides validation. Validation is the process of checking the values entered by the user against constraints. Example constraints could be to check whether a value is between the specified limits.
C++
void CDataExchangeDlg::DoDataExchange(CDataExchange* pDX){ //should precede the DDX and DDV routines CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_nAge); DDV_MinMaxInt(pDX, m_nAge, 18, 45); DDX_Check(pDX, IDC_CHECK1, m_bCheck);}
The above code checks whether the value entered by the user lies between 18 and 45. If the value lies outside the specified limits, then the user is shown a message box to indicate the error, and Fail ()
is called (which sets the focus to the control and throws an exception). It's to be noted that a DDV routine for a given control must be called immediately after the DDX function for the same control (the reason being that, in case of a failure, the focus will be set to the offending control).
Like DDX routines, we can have our own custom DDV routines. The following checks whether the number is even or not.
C++
void DDV_IsEven(CDataExchange* pDX, int value) { if (value % 2 != 0) { AfxMessageBox("Enter an even number.", MB_ICONSTOP); pDX->Fail(); }}
History
- 19th June, 2006: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.