mercredi, juin 15

WTL Custom DDX

The DoDataExchange (DDX) is a recognizable method for initializing and/or saving dialog data, especially for GUI developers who have had experience in MFC through the years.

The existing DDX macros available however may not be sufficient to accomodate all types of scenarios.

Take for example a combo or a date time picker control. There are no existing DDX macros for them, developers especially those who aren't familiar with how DoDataExchange works, do the process of reading and writing to the controls upfront.

With custom DDX, developers are able to define their own macros depending on the situation which keeps the code simple encapsulating the arduous task of reading and writing data.

Below is a sample of how it might look like in practice. Notice that there is a clear distinction between the dialog controls vs the dialog data. Never mix the two. Example: m_btnMarried is an instance of check box button while m_bMarried is the value of the check box button.
// Controls
CButton m_btnMarié;
CEdit m_editNom;
CComboBox m_comboTitre;
CDateTimePickerCtrl m_dtpAnniversaire;

// Dialog data
bool m_bMarié;
wstring m_strNom
enumTitre m_eTitre;
SYSTEMTIME m_stAnniversaire;

BEGIN_DDX_MAP(CMaView)

DDX_CONTROL_HANDLE(IDC_CHECK, m_btnMarié)
DDX_CONTROL_HANDLE(IDC_EDIT, m_editNom)
DDX_CONTROL_HANDLE(IDC_COMBO, m_comboTitre)
DDX_CONTROL_HANDLE(IDC_DATE, m_dtpAnniversaire)


// Exiting DDX
DDX_CHECK(IDC_CHECK, m_bMarié)
DDX_TEXT(IDC_EDIT_NOM, nom)

// Custom DDX
DDX_COMBO(IDC_COMBO, (int&) m_eTitre)
DDX_DATETIMEPICKER(IDC_DATEPICKER, m_stAnniversaire)

END_DDX_MAP()

Without using the DDX framework, the developer needs to call the following lines upfront to be able to get to the value specified on the controls.
m_btnMarie.GetCheck()
m_editNom.GetWindowText()
m_comboTitre.GetCurSel()
m_dtpAnniversaire.GetSystemTime()

In contrast, using the DDX framework, the developer needs only to call DoDataExchange(FALSE) to initialise and DoDataExchange(TRUE) to update dialog data.

Of course, there is no DDX_COMBO and DDX_DATETIMEPICKER, we need to define it ourselves. Open the atlddx.h file and define the custom DDXs.

Inside the DoDataExchange method ..
//
// Custom DDX begin
//

#define DDX_DATETIMEPICKER(nID, var) \
if(nCtlID == (UINT)-1 || nCtlID == nID) \
{ \
if(!DDX_DateTimePicker(nID, var, TRUE, bSaveAndValidate)) \
return FALSE; \
}

#define DDX_COMBO_INDEX(nID, var) \
if(nCtlID == (UINT)-1 || nCtlID == nID) \
{ \
if(!DDX_Combo_Index(nID, var, TRUE, bSaveAndValidate)) \
return FALSE; \
}

//
// Custom DDX end
//

Inside WTL::CWinDataExchange class
//
// Custom DDX begin
//

template
BOOL DDX_Combo(UINT nID, Type& nVal, BOOL bSigned, BOOL bSave, BOOL
bValidate = FALSE, Type nMin = 0, Type nMax = 0)
{
T* pT = static_cast(this);
BOOL bSuccess = TRUE;

if(bSave)
{
CComboBox combo;
combo.Attach(pT->GetDlgItem(nID));
nVal = combo.GetCurSel();

bSuccess = (nVal == CB_ERR ? false : true);
}
else
{
ATLASSERT(!bValidate || nVal >= nMin && nVal <= nMax);

CComboBox combo;
combo.Attach(pT->GetDlgItem(nID));
int iRet = combo.SetCurSel(nVal);

bSuccess = (iRet == CB_ERR ? false : true);
}

if(!bSuccess)
{
pT->OnDataExchangeError(nID, bSave);
}
else if(bSave && bValidate) // validation
{
ATLASSERT(nMin != nMax);
if(nVal < nMin || nVal > nMax)
{
_XData data;
data.nDataType = ddxDataInt;
data.intData.nVal = (long)nVal;
data.intData.nMin = (long)nMin;
data.intData.nMax = (long)nMax;
pT->OnDataValidateError(nID, bSave, data);
bSuccess = FALSE;
}
}

return bSuccess;
}

template
BOOL DDX_DateTimePicker(UINT nID, Type& value, BOOL bSigned, BOOL bSave, BOOL
bValidate = FALSE)
{
T* pT = static_cast(this);
BOOL bSuccess = TRUE;

if(bSave)
{
CDateTimePickerCtrl dtp;
dtp.Attach(pT->GetDlgItem(nID));
DWORD dwSuccess = dtp.GetSystemTime(&value);

bSuccess = (dwSuccess == GDT_ERROR ? false : true);
}
else
{
ATLASSERT(!bValidate);

CDateTimePickerCtrl dtp;
dtp.Attach(pT->GetDlgItem(nID));
bSuccess = dtp.SetSystemTime(GDT_VALID, &value);
}

if(!bSuccess)
{
pT->OnDataExchangeError(nID, bSave);
}

return bSuccess;
}

//
// Custom DDX end
//

Aucun commentaire: