Dynamic Data Exchange (DDX) Internals

In general, I am a fan of the way DDX works. It is quite a neat design and has many good points going for it. In particular, it neatly encapsulates the transfer of data between class member variables and dialog controls in a single place, and with a minimum of code.

Let us take a quick look at what the DDX mechanism does and how it works.

A dialog, property page, or form will usually have a number of controls on it. Otherwise, it would not be very interesting. Most of these controls, such as edit controls, let you display and / or edit text. However, that text lives inside the control itself. For it to be useful to you, you need to get your own data into and out of the control at the proper time. That is where DDX comes into play.

There are three main parts to DDX; UpdateData, DoDataExchange and the DDX functions themselves.

You call the UpdateData member function to either get data from or put data into a control. You specify the direction of the transfer by a BOOL argument to the UpdateData function. Using a BOOL for this is, in my opinion, a bad design decision. The designers of MFC should have used an enum with appropriately named values instead. I personally always forget what the value needs to be, and am forever looking up UpdateData in the on-line help to remind me (you'd think I'd remember by now though, wouldn't you).

Of course, what I can do is define my own enum, or even just simple #defines, for the values. For example:

#define FROM_CONTROLS_TO_MEMBERS TRUE
#define FROM_MEMBERS_TO_CONTROLS FALSE

Then I could just write:

UpdateData(FROM_CONTROLS_TO_MEMBERS);

which is much more obvious than:

UpdateData(TRUE);

The next main part of DDX is the DoDataExchange virtual function. UpdateData sets up an object called a CDataExchange that holds information about the direction of the transfer and so on and passes this to DoDataExchange to do the work. The body of DoDataExchange is generally just a whole lot of DDX (and DDV) calls.

DDX functions themselves are just global functions that do the real work of transferring the data for each individual control. There is nothing very special about them. The DDX routines for exchanging values generally entail using GetDlgItem to find the item by ID number, and calling GetWindowText and SetWindowText appropriately.

When it comes to writing your dialog class, you simply call UpdateData(TRUE); (or use a #define to make it more obvious) at the start of a function where you want to get values from the controls and do something with them. Similarly, you call UpdateData(FALSE); at the end of a function when you want to copy updated values back in again. All you then need to supply is appropriate DDX lines in your DoDataExchange function to set up the relationships.

Before going any further, there is a special DDX function called DDX_Control that I should at least mention. Rather than exchanging data, this DDX function associates a CWnd-derived member variable with a control. This is association is called subclassing (it is one of several uses of this term) and the happens on the first UpdateData(TRUE) call. This call is usually the one that CDialog::OnInitDialog makes for you. Once the control and the member variable are associated, the DDX_Control call does nothing, and you can then access the control directly using the member variable. This is useful is particularly useful if you have derived you own class from an appropriate MFC CWnd-derived class and want it to applt to a particular control. It is also useful for doing things such as enabling and disabling controls. Anyway, I will not go any further into DDX_Control, as it is not relevant for this particular discussion.

Therefore, as I said earlier, the DDX mechanism neatly encapsulates the transfer of data between class member variables and dialog controls in a single place, and with a minimum of code.

However, it does lack some flexibility when it comes to numeric values, particularly float's and double's. The problem is that the built-in DDX routines do not let you specify a format. For example, if you want to show exactly two decimal places for you numbers, you are out of luck.

One common way around this is to use a CString and convert your double or float value to and from the CString as appropriate. As always, there are number of ways of doing this, some of which are 'better' than others.

The most obvious is to put code to convert your CString to a numeric value code after you UpdateData(TRUE), and to convert from numeric to CString just before the UpdateData(FALSE). For example:

void CMyDialog::MyFunction() {
 UpdateData(TRUE);
 sscanf(m_text, "%.2f", &m_dval);
 // do work with m_dval
 m_text.Format("%f", m_dval);
 UpdateData(FALSE);
}

This will work, but has the disadvantage of duplicating this code in every function.

Another way is to put the conversion in its own function. Here I have copied the MFC UpdateData conversions. I would rather be consistent with MFC than try to use an enum (even though, as I mentioned above, I feel that using a BOOL is not good design).

void CMyDialog::ConvertData(bool bSaveAndValidate) {
 if (bSaveAndValidate) {
  sscanf(m_text, "%.2f", &m_dval);
 } else {
  m_text.Format("%f", m_dval);
 }
}
void CMyDialog::MyFunction() {
 UpdateData(TRUE);
 ConvertData(TRUE);
 // do work with m_dval
 ConvertData(FALSE);
 UpdateData(FALSE);
}

Now you can have all your conversion code in the one place. However, you still have to remember to call it. The next improvement should be just screaming out at you. Put the conversion calls somewhere inside the UpdateData processing itself. The obvious place being in the DoDataExchange function that you are (almost certainly) already overriding. Even thought your DoDataExchange function probably looks like a bunch of DDX lines within some cryptic // {{ AFX_DATA_MAP comments, it is still just a normal member function that can have good old-fashioned C++ code in it. And as long as you don't go adding non-DDX lines between those AFX_DATA_MAP comments, Class Wizard will still be happy.

Now you can end up with code like this:

void CMyDialog::ConvertData(bool bSaveAndValidate) {
 if (bSaveAndValidate) {
  sscanf(m_text, "%.2f", &m_dval);
 } else {
  m_text.Format("%f", m_dval);
 }
}
void CMyDialog::DoDataExchange() {
 if (! bSaveAndValidate)
  ConvertData(bSaveAndValidate);

 CDialog::DoDataExchange(pDX);

 //{{AFX_DATA_MAP(CXAboutDlg)
 DDX_Text(pDX, IDC_DVAL, m_text);
 //}}AFX_DATA_MAP
 
 if (bSaveAndValidate)
  ConvertData(bSaveAndValidate);
}

Now we just need to call UpdateData in our functions as usual.

However, we still have to remember to write the ConvertData function and add it to our DoDataExchange function for every dialog. To save us from this drudgery, we can derive our own CDialog-derived class that adds ConvertData as a virtual function (which does nothing by default) and call it from DoDataExchange in the same class. We could even use the technique from the last article to provide a template class with multiple-inheritance to add the same functionality to property pages and form view.

However, a nicer way would be to just write our own DDX function that takes a format. This is fairly straightforward to do, and would be even easier is the MFC designers had not made all the nice little helper functions that the built-in DDX routines use into local statics, which means we cannot use them ourselves. But, in this case, it is not too much of a problem, as we can call the built-in in DDX_Text function to do the bulk of the work for us.

void DDX_Text_Formatted(CDataExchange* pDX, 
                        int nIDC, 
                        double& value, 
                        LPCTSTR lpszOutFormat=_T("%f"), 
                        LPCTSTR lpszInFormat=_T("%lf"))
{
 CString temp;
 
 if (! pDX->m_bSaveAndValidate)
  temp.Format(lpszOutFormat,value);
 
 DDX_Text(pDX,nIDC,temp);
 
 if (pDX->m_bSaveAndValidate)
  sscanf(temp,lpszInFormat,&value);
}

Now we can use DDX_Text_Formatted wherever we would have used DDX_Text for double float or other values and specify the format that we want. One can also provide other DDX functions that call DDX_Text_Formatted with specific output format strings (such as forcing two decimal places).

There are still two remaining things to note.

Firstly, due to the strangeness of the C++ language and the C runtime library, Format (and sprintf etc) make different assumptions about the size of a value specified by a "%f" format type. Format (and spprintf etc) assume that "%f" always means a double, whereas sscanf assumes that "%f" means a float unless you prefix the 'f' with an 'l'. So, if you end up providing you own value for lpszInFormat, make sure you use the correct format specifier or you will end up with garbage results!

The second point is a problem shared with the built-in DDX routines as well. If you call UpdateData(FALSE) while the user is trying to enter a value (for example, in a ON_EN_CHANGE handler), then you can stuff up the cursor position. The reason for this is that whenever you call SetWindowText, the cursor goes back to the start of the edit field. DDX is usually smart enough not to call SetWindowText unless the text has actually changed. But, with floating point numbers, there is a problem. If you type the characters "123.0" into an edit box with a DDX_Text for doubles, and you are calling UpdateData(FALSE) after each character (in an ON_EN_CHANGE handle), then all goes well until you type the '.'. When you get to the '.', the cursor jumps back to the start of the string and the '.' does not appear!! The reason is that after typing the '.', the numeric value of the entry is 123 which, when converted to a string, is "123'. So that means the call to UpdateData(FALSE) will in turn call DDX_Text and try to change the edit box text from the "123." that you just typed in into just "123". The text value is different, so DDX_Text calls SetWindowText to update the edit box, and that resets the cursor back in front of the first character in the edit box. This behaviour makes it very difficult to enter floating-point numbers. For this reason, try to avoid EN_CHANGE and similar messages for numeric edit boxes.

So, we've had a look at how DDX works, how to improve it in the area of floating point number formats, and seen an interesting 'bug' in the way DDX handles floating point number. That is enough for this article see you next time.



Comments

  • Wat is er zo goed over hoofdtelefoon

    Posted by mrswanzi on 06/05/2013 08:11pm

    [url=http://koptelefoon-monsterbeats.weebly.com/]beats by dre kopen[/url] Imitaties van de Beats By Dr Dre koptelefoons worden ontzettend goed gemaakt. Veel van de neppe beats by dr dre¡¯s zijn uiterlijk niet van echt te onderscheiden. Imitaties zijn soms moeilijk van echt te onderscheiden en er zijn ook een aantal Myhten en Fabels over het herkennen van imitaties. Als je zeker wilt weten dat je een origineel exemplaar aanschaft, zorg dan dat je bij een geauthoriseerde dealer koopt. Zo krijg je de garantie dat je een authentieke Beats By Dr Dre koopt. [url=http://koptelefoon-monsterbeats.webspawner.com/]beats by dre kopen[/url] De samenwerking met LeBron James past perfect in de endorsement marketing strategie die Monster Cable en Beats Electronics, het bedrijf van Dr. Dre en muziekproducent Jimmy Iovine, hebben uitgedacht. Nog voor de headphones voor de consument op de markt verschenen, liepen verschillende grote artiesten uit de Amerikaanse hip hop- en rapscene al met een Beats by Dr. Dre rond. [url=http://monsterkoptelefoon.npage.de/]beats by dre kopen[/url] De urBeats zijn wederom ontstaan uit een perfecte samenwerking tussen Monster en Dr. Dre. Zodoende zijn ook deze in-ears verschenen onder het Beats By Dr. Dre label, waar al meer succesnummers onder zijn uitgebracht. Deze voorzien allen in unieke audioprestaties en een stijlvol design. Het is ook daarom dat deze urBeats de titel met trots mogen dragen!

    Reply
  • Honest guide brings out Some completely new things over nike that absolutely no one is covering.

    Posted by BobHotgloff on 05/16/2013 04:59pm

    A person's double take on swimwear [url=http://plus-size-swimwear.webnode.com/]modest swimwear[/url] Extraordinary summary provide you with the truth around swimwear that experts claim not very many customers are aware of. [url=http://modest-swimwear.webnode.com/]speedo swimwear[/url] Neutral editorial lets out Six brand-new stuff on swimwear that noone is talking about. [url=http://spanx-swimwear.webnode.com/]designer swimwear[/url] Howcome all are extremely wrong concerning swimwear and consequently the reasons why you ought to read through this post. [url=http://vitamin-a-swimwear.webnode.com/]vitamin a swimwear[/url] As to why all things you have found out about swimwear is almost certainly wrong and what you should be aware of. Upcoming swimwear E book Unearths Proven Methods To Dominate The swimwear Market [url=http://girls-swimwear.webnode.com/]girls swimwear[/url] Brand new queries about swimwear have been answered and in addition the reasons you ought to check out every single statement in this document. [url=http://freya-swimwear.webnode.com/]freya swimwear[/url] Those things that the competition has been doing with regard to swimwear and those actions youought to do different. [url=http://victoria-secret-swimwear.webnode.com/]bra sized swimwear[/url] Innovative new swimwear E book Exposes Guidelines On How To Dominate The swimwear World [url=http://cheap-swimwear.webnode.com/]swimwear sale[/url] Innovative essay presents you with the low down for swimwear and as well as reasons why you must take action straight away.[url=http://women-swimwear.webnode.com/]thong swimwear[/url] Bizarre essay will give you the run data on swimwear which just one or two consumers are aware of.

    Reply
  • NikeFree again four shoes be attracted to in autumn and winter corrode ease is barest fair

    Posted by Speadyday on 04/25/2013 08:04am

    Nike Free TR Fit 2 Screen broke of textile materials in wire with seasonal requirements, enhances the warmth and durability, and cogitative constituents, in [url=http://www.nikeskoroutlet.se/nike-free-trainer-50-c-48/]nike free trainer[/url] barren visibility when elucidation and DWR (firm deuterium oxide unruly) coating to carry out breathability while, let feet wilt in wet weather. Shoe more northerly knock on the inside of of the bow down effervescence enhances foot buttress and stability. Aspect correspondence of the Nike Unrestricted shoe Channel conceive provides stretchable like manifest feet handle and determination of multi-direction displacement while [url=http://www.nikeskoroutlet.se/nike-air-max-2013-c-49/]air max 2013 sverige[/url] retaining tenure and shockproof main attraction inherited in training shoes. Lightweight Phylite midsole can look after and masses of durability, which increments the usefulness exponentially outsole, which significantly reduces the manipulate of the shoe. Down and foot rubber grooves made of environmentally buddy-buddy materials, stop to enhance its [url=http://www.nikeskornatet.se/nike-air-max-skyline-c-5/]air max skyline[/url] multi-directional grip on all kinds of route surfaces.

    Reply
  • Portals Nous fournissons Hoodies Abercrombie et Fitch, T-shirts, polos, jeans, shorts Supervised

    Posted by Vetriatszy on 03/16/2013 10:09am

    may have a great deal of criteria exams or discount codes to alternate produce other coupons you want to replace clients? include effective baby shirts, equipment, video games or maybe supplementary actuals to help you commercial? bring about a person's day smarter by providing a RAOK? satisfy added forex traders and see what they have to offer. We can also get the splendid ~*~ ISO the following friday ~*~ place of personal groups. i know every one of us expect mondays but now they should be in addition to this! do not forget to visit on monday and see wheat berry dig this become different for the more effective! please be aware: each of our reviews enter not necessarily roam none endorsed created by BaCenter. issues different fears near individual health or the fitness of your children, stomach muscles consult with a physician or possibly a additional healthcare professional. want assess the privacy moreover relation to Use in advance of using this site. your favorite standby and call time site has revealed binding agreement in order to always going with all the terms of Use

    Reply
  • Great article

    Posted by Legacy on 01/27/2004 12:00am

    Originally posted by: Sabari

    Simple and very useful article.

    Reply
  • Good Article

    Posted by Legacy on 06/27/2002 12:00am

    Originally posted by: Prasad

    A very good article and one i have been searching for.
    -thanks

    Reply
  • DDX does not work in Win2000

    Posted by Legacy on 12/03/2001 12:00am

    Originally posted by: aGaucho

    It appears that the "DDX" function does not work in Win2000, please revise and or suggest a solution.

    Reply
  • Another suggestion to correctly remember the appropriate value for calling UpdateData ()

    Posted by Legacy on 05/26/2001 12:00am

    Originally posted by: Sam Hobbs

    We often process data from a dialog in the sequence:

    (1) UpdateData(TRUE) - get data
    (2) process
    (3) UpdateData(FALSE) - put data

    Notice that the Final thing done is the FALSE one.

    Reply
  • Just a suggestion to correctly remember the appropriate value for calling UpdateData ()

    Posted by Legacy on 04/23/2001 12:00am

    Originally posted by: Christian Batlle

    Just let us distinguish between the

    DIALOG

    on one side, with all its GUI elements, i.e. controls, etc.
    and the

    DATA

    on the other side, which is displayed by the dialog controls, and may be changed by the user.

    When the data is read from the dialog, let's say we are

    updating the data => so UpdateData (TRUE) is used.

    And when the data is written/ transferred to the dialog,
    let's say we are

    updating the controls => so UpdateData (FALSE) is used.

    Updating the controls means the control's contents, which could be done by an imaginary UpdateDialog () function, which doesn't exist, or, to just do it all calling just one function, with UpdateData (FALSE).
    FALSE, because it's not the data which is being updated, but the dialog.

    Hope this helps

    Regards

    Christian Batlle

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

  • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds