Using DDX and DDV with WTL

Introduction

One of the more tedious parts of UI coding is simply doing the grunt work of passing member data into various dialogs for user interaction, and then pulling it back out and ensuring the data is valid before recommitting it to the core part of your program. MFC realized this, and has long had the concept of DDX (dynamic data exchange) and DDV (dynamic data validation). However, those of us who are focused on download size stick to ATL and SDK style coding, meaning lots of rote code has to be done to enable the user to modify various properties and settings in our UI dialogs. With WTL, however, we now have the opportunity to gain a lot of the ease of MFC coding without the bulk. This article will show you how to make use of WTLs DDX/DDV implementation, show two custom extensions I added to WTLs implementation to extends its reach, and provide code taken from a real-life example using WTLs property sheet implementation (CPropertyPageImpl).

What is DDX/DDV exactly?

As alluded to earlier, the purpose of DDX is to provide a framework for handling the passing (both loading and retrieving) of data between your application and the UI it presents to the user via dialogs, property sheets, etc. The goal of DDV is to enable automatic verification of any changes the user made to ensure they have entered valid data, where valid is defined by your application. The benefit of using DDX is efficient, easy to read and maintain mappings between your applications variables and their presentation/modification in your various dialogs. Additionally, you will see a significant reduction in the amount of time you spend coding the basic infrastructure needed to support interactive UI.

With the overview out of the way, lets get into coding to start explaining the details involved in utilizing DDX in your app. WTL provides DDX/DDV handling via the header atlddx.h, which contains macros for creating a DDX map (same concept as ATLs message map), and the templatized class CWinDataExchange.

Thus the first step to make use of DDX/DDV is

#include <ATLddx.h>

into your StdAfx.h or other primary header file. The one caveat here, is that if you are using WTLs Cstring, you must include AtlMisc.h before AtlDDx.h.

Thus, I always simply add the following to my StdAfx.h

#include <atlmisc.h> //CString support
#include <atlddx.h>

in StdAfx.h, right beneath atlwin.h and forget about it. Because the sample code in this article will also use property sheets to demonstrate ddx, we have to add the include for atldlgs.h, giving us a final StdAfx.h that has this segment of code in it:

After that, we can now get to the heart of it, which is actually connecting your applications variables to their respective UI components. This is done via the DDX message map, of which a simplistic example is listed below:

Within the message map is where you direct WTLs DDX class how to hook up your variables to your dialogs UI elements. There are a number of macros that can be used for various data types, but lets briefly look at the two entries above. The first one DDX_TEXT, simply states that the value in <string variable> should be assigned to <dlg resource id> on load typically a string variable mapping to an edit box.

On the close of the dialog, the mapping is done in reverse the current contents of <dlg resource id> are pulled out and placed back into <string variable>. Very nice and tidy. The second macro shown above, DDX_TEXT_LEN has similar functionality to DDX_TEXT in that it joins the <dlg resource id2> to <string variable2> but you can see there is a third parameter, <max text length>. Specify a value here, and if the users text entry exceeds it, the DDV error handler will kick in. You can override the default handler, or you can use the default handler, which will beep, and set the focus back to the offending control to prompt the user to correct it. (You implement your own handler by overriding the function void OnDataValidateError(UINT id, BOOL bSave,_XData& data), which is demonstrated in the sample app).

There are a number of macros for the message map, usually with a variation of one for pure linkage (ala DDX_TEXT) and one for linkage and validation (ala DDX_TEXT_LEN). See summary at the end of this article.

With that, the final step is to actually tell WTL when to fire the actual data exchange. This is done by calling DoDataExchange(BOOL fParam) with FALSE to load the dialog with your data values, and TRUE to retrieve the data from your dialog. Where to do this is up to you, but OnInitDialog (handler for WM_INITDIALOG) is a good spot for the DoDataExchange(FALSE), or load call. For pulling back the modified data, you could put the DoDataExchange(TRUE) call (retrieve) in OnOK for a dialog, and for property pages, you probably want to handle it in OnKillActivate().

DDX in Action

With an understanding of the fundamentals involved in utilizing DDX/DDV, lets move on to the sample application, where we can see DDX in action, as well as examine some limitations I found and how to work around them.

The sample application is based off of a subset of code from a commercial application that handles the viewing/monitoring of multiple wireless cameras. The relevant part of this app is that it has to allow the user to specify individual settings for up to 16 cameras and do so in a clean UI. The solution I chose was to use WTLs property page implementation to allow a nice tabbed dialog for each camera, and then use DDX/DDV to simplify the transfer back and forth of the individual settings. The sample app simply lets you play with four settings, so that you can see in the debugger how the settings are transferred and validated. Ill give an overview here of some of the more interesting parts, and after that, just tracing the code in the debugger should cement your understanding of DDX/DDV.

Because the camera settings were going to be globally used throughout the app, I made a global struct as follows (from stdafx.h):

Since we have to scale up to 16 cameras (four in the sample though), I modified the constructor of the CcameraBase class to take an integer to identify what camera it was handling:

CCameraBase(int _index)
{
m_iIdentity = _index;
...
}

With that done, we now have a basic layout for a property page, a framework for transferring the data and pulling it back after the user has modified it, and an index to allow us to reuse the same class for all cameras. Now, to actually implement this 4x in our UI, we turn to our derived CpropertySheetImpl class, CcameraProperties. There isnt too much to it as shown below:

Note the use of the member initialization list above in bold to show where we actually id each of the class instances with their respective camera. After that, call AddPage to hook up the classes into the tabbed layout, and specify your first page via SetActivePage. There is a small message map omitted above, but beyond that, you now have the handler for the full property sheet.

Extending DDX in WTL

Of course, things didnt all just fall into place there were two immediate problems I hit that required adding new extensions to to get the DDX framework to do some additional handling. The first item was regarding the combo boxes the generic ddx implementation would be to specify DDX_INT and pass in the combo box id to handle the mapping of the cameras unit code & house code to their respective combo box. However, the gotcha is that DDX_INT calls Get/SetDlgItemInt underneath, which merely places or retrieves the textual representation of an integer. For the cameras, which use X10 wireless protocol, both the unit code and house code represented an index into an array of values, not the values themselves.example: house code A is represented by 0, B is 1, etc. Under the default DDX implementation, if I passed in its intrinsic value 0, I would get a combo box showing 0, which is not what I wanted.

Since I think its very common to have combo boxes representing indexes into arrays or enums rather than trying to represent the literal textual value, I added a new macro and macro handler called DDX_COMBO_INDEX. This will handle the passing and retrieval of an index value, rather than the literal textual translation. The sample has the modified code, but it ended up like this:

With this, I could successfully handle indexes within my combo box UI. The other gotcha was that the UI I wanted to use was to have two radio buttons, representing a true/false choice for the user (see the option #4, do you wish to have timestamps added). Initially I thought DDX_RADIO was what I wanted, but that didnt do it, and neither did DDX_CHECK (didnt handle the toggling of the UI to create an exclusive selection between the two radio buttons). Thus, I added another extension, DDX_BOOL_RADIO, which took two resource ids as follows:

What this extension does is ensure that in the load, only one of the two buttons is selected, based on the state of the bool variable. If true, the primary ID radio is checked, the secondary radio button is initialized to unchecked, and the reverse if the initial load value is false. Obviously, you could simply use one radio button to represent the true/false state, but I wanted to make it really clear to the user what they were selecting by explicitly calling it out with two buttons and associated text. You can also find the code for DDX_BOOL_BUTTON in the modified file.

With that, the basic code framework for the sample application is complete we now have a UI that can elegantly handle the transfer back and forth of data between UI and internal variables via WTLs DDX/DDV, allow the user to understand what they are selecting via a tabbed property dialog, and finally, we have a codebase that can scale too any number of cameras with minimal change to the code.

Hopefully, a quick run of the sample app under the debugger will clear up any remaining questions, and you will now be able to take advantage of WTLs DDX/DDV framework to ensure you no longer have to keep writing reams of rote GetDlgItem/SetDlgItem style code for your future UI.

If you have recommendations for improving this article, or write your own extensions to atlddx.h, I would appreciate hearing about them. You can email me at less_wright@hotmail.com

and "Connect" it to the existing ListViewControl with DDX_CONTROL( IDC_MYLIST, CMyListViewCtrl ). The Messages are not mapped to the Message Handling Macros in CMyListViewControl.

That's the way it's done by class wizard when using MFC.
What did I wrong ? Any ideas ?

I don't want to implement it as contained window, because: (1) I have to do the creation "by hand" at runtime,
(2) I need the class in some more dialogs and don't want to "copy" the message handlers into each dialog ...

Top White Papers and Webcasts

Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT
The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility.
Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

Companies undertaking an IT project need to find the right balance between cost and functionality. It's important to start by determining whether to build a solution from scratch, buy an out-of-the-box solution, or a combination of both. In reality, most projects will require some system tailoring to meet business requirements. Decision-makers must understand how much software development is enough and craft a detailed implementation plan to ensure the project's success. This white paper examines the different …