Click here to Skip to main content
15,887,083 members
Articles / Desktop Programming / MFC
Article

Avoiding UpdateData

Rate me:
Please Sign up or sign in to vote.
3.43/5 (29 votes)
23 Apr 2007 257.5K   41   49
Learn how to avoid using UpdateData in your modal dialogs.

Introduction

Microsoft does not adequately document the correct way to work with controls. It is left up to the programmer to somehow magically infer the correct way to do this. Unfortunately, all of the evidence suggests that UpdateData is the correct way to handle the problem of accessing control information while working inside a dialog. This is not correct. In fact, it is dangerous to use it. It is simply the wrong way to go about it.

After I posted this essay, one reader, Doug Harrison, sent me a critique. I'm including his comments at the end, because some of them point out some potential flaws in my approach, and are valid points. I also disagree with a couple, and I'll tell you why, and you can judge for yourself what you want to do. But a Wave of the Flounder Fin to Doug, who took the time to send me a message.

You should never call UpdateData in a modal dialog. Just Don't Do It. Ever. There are many reasons. One reason is that the OnOK handler calls UpdateData to store the control values in the associated member variables. The OnCancel handler does not call UpdateData. Therefore, the assumed behavior, or the should-be-assumed behavior, is that if you set a collection of member variables before calling DoModal, upon successful completion of the dialog the member variables will hold the new control values, and upon error completion the member variables will have the same values that you put into them. This makes calling a modal dialog very simple:

C#
CMyDialog dlg;
dlg.m_Count = somecounter;
dlg.m_Text = sometext;
dlg.m_Option = someBool;
dlg.DoModal( );
somecounter = dlg.m_Count;
sometext = dlg.m_Text;
someBool = dlg.m_Option;

Nothing to it! But if you ever call UpdateData yourself, this simple paradigm won't work. That's because if the user clicks Cancel, you've already messed over the values to represent some intermediate state that the user has just chosen to reject.

Doug Harrison sent me a critique which points out a serious flaw in the above sequence. It is a flaw I never knew about because I have never used a particular feature of dialogs (well, I used it, didn't like it, and chose to deliberately avoid it). But his critique is well-taken, and you should read it. I've included it at the end.

Another reason to avoid UpdateData is that UpdateData restores all the state. This means you have to remember to save that state! I found this the greatest problem I had in managing the use of UpdateData before I figured out that it was simply a Bad Idea. You have to make sure, every time a change is made, that you copy all the values out of the controls, via UpdateData, so that if you want to make another change and restore the values you have the correct set, not some mix of some previous values and current values. This drove me crazy. It will probably drive you crazy, too.

In addition, if you call UpdateData, it snaps values from memory into all of your edit controls. This will immediately cause a set of OnChange (WM_COMMAND/EN_CHANGE) notifications, as each edit control is returned. Now assume that there is some issue about consistency checking, such that modifying control A affects the value in control B. But if you do UpdateData, you have to deal with the fact that this flurry of events may happen in an order other than the expected order from a user, and you have to make all sorts of provisions to deal with it, such as always setting a flag that indicates if the change should be reacted to or not.

Overall, I found that the three programs I wrote using UpdateData were harder to write, harder to debug, and much harder to maintain, than any program before or since in which I did not make any use of UpdateData. To me, ease of maintenance is one of the most important concerns. Low cost of development is also important.

The correct way, I have found, is to use control variables. If you don't understand what a control variable is, click the hyperlink to read my essay on them.

So how do you get the values of the controls while the dialog is active? Just use the control variable to access the control! For example, to determine if the OK button should be enabled, and it can only be enabled if the text value is nonblank you can write:

C#
CString s;
c_Text.GetWindowText(s);
s.TrimLeft();
c_OK.EnableWindow(s.GetLength() != 0);

Far easier than having to remember how to call UpdateData (is it TRUE or FALSE for the argument?) and avoids the danger of messing over the OK/Cancel behavior.

Where do you write the above code? Where do you write the above code? Well, that's another essay!

OK, I'll relent, just a bit, and tell you another reason to not use UpdateData, which is the only reason I have found to use UpdateData (keep reading, that's not a typo that makes the sentence look contradictory!) I have a client who is offended by the situation where you have made some changes in a dialog, gotten confused, and had to click Cancel to get out of the dialog, only to immediately re-enter it to start over. He insisted that every dialog in his product have a Reset button that reset the dialog to its original state. If you avoid UpdateData this becomes easy: you use UpdateData. What I mean is, if you avoid gratuitous, pointless, useless, UpdateData calls (that is, in almost every dialog I can think of, any call on UpdateData), then the original information is left intact, and a single UpdateData call will restore all the original values. But note that the only use of UpdateData is to load the controls with the original input values, and this will not work if you have used it to grab the values back at intermediate points; in this case, you have destroyed the input state. Actually, I like his style, and so, like most dogmatists, I have to say "but there is an exception". Thus far, it is the only exception I've found for non-database applications (see Doug Harrison's comments, which follow below). Since I have written, literally, hundreds of dialogs in the last five years or so, and after the first three applications have never called UpdateData, with the one exception of resetting all of the state to the original input state, I cannot get terribly excited about any claims that this call is the right way to do general dialogs. Or that an explicit call of UpdateData even has any place in them, other than the reset case.

Some Comments from Doug Harrison

Doug Harrison took the time to reply to my essay. He raises some interesting points, and one of them shows that I haven't used a number of dialog features which can lead to some problems. I include a slightly edited version of his comments here, so you can read them and make your own choices.


Doug writes:

I haven't read all your essays, but I do have some comments concerning your OnUpdateData essay.

There are some problems with the sequence below:

C#
CMyDialog dlg;
dlg.m_Count = somecounter;
dlg.m_Text = sometext;
dlg.m_Option = someBool;
dlg.DoModal( );
somecounter = dlg.m_Count;
sometext = dlg.m_Text;
someBool = dlg.m_Option;

1. You shouldn't rely on users to initialize the dialog members directly. As with almost every class, it's much better if the ctor has that responsibility.

This is one I disagree with. Only the caller knows what the values are, the default constructor cannot possibly know any details of the calling site. To avoid this, you have to modify the constructor by hand to add the necessary parameters. This, at least the one time I tried it a few versions back, horribly confused the ClassWizard, which proceeded to corrupt the source file.It made me sufficiently nervous that I've not tried it again. The technique of using the constructor, which he shows below (and, which in my opinion as well is a better way to do it), still requires pulling the data out afterward. Microsoft works in an illusory world in which all of these parameters have to be simple scalars, a restriction so brain-dead that I cannot imagine why anyone could ever imagine it is sufficient. I'd rather pass in a pointer to a structure, but then I can't use ClassWizard to deal with the value variables, because it won't parse structure accesses, or anything other than a simple variable. Since I still have to set everything up, and pull it back out, I prefer the symmetry of seeing the assignments. My inclination would be to have a ClassWizard which would allow me to specify a list of parameters which would be passed in as references, and which would update the variables only upon completely successful completion (that is, all validation was passed). In the absence of something useful, I consider the names of the member variables to be initialized to be part of the specification of the interface. Note that only place I ever use the m_ prefix is a member variable which is used to pass parameters to the dialog from the caller; in no other place anywhere in any dialog I write will you ever find an m_ variable that is used for any other purpose. So I disagree with this point, but only because the tools are still primitive. The bottom line is that Microsoft has a long way to go in providing the necessary automation. In the absence of decent automation, I prefer to do it myself. I'm not opposed to automation, just to poor automation. --jmn

2. If you care about the values of those members upon return from the dialog, you must care what DoModal returns. To see why, consider that if there's a validation error, UpdateData doesn't run to completion when called from OnOK, and the user can subsequently exit the dialog by canceling it. The code above is subject to storing a mix of new and old values in that scenario, despite the user canceling the dialog, depending on which DDV call in DoDataExchange failed, as well as the order of the DDX/DDV calls.

Thus, the code above is better written as:

C#
CMyDialog dlg(somecounter,sometext,someBool);
if (dlg.DoModal() == IDOK)
   {
    // User said OK, and validation succeeded, 
    //so update application state
   }

In this point he is absolutely correct. Since I never use DDV, for what I think are sound reasons, I would never have encountered this case. If you do use DDV, or think you might use DDV in the future, you need to use the more formally correct form he shows here.--jmn

Also, I can't agree with the premise, "Never call UpdateData." The function definitely has its uses, even in modal dialogs. For example, consider a dialog box that (in part) contains some controls that provide an interface to a simple database of (key,item) pairs, such as an STL map. There is a Save button, and pressing it saves the current record.

I must admit that I've never wanted to do this; I tend to favor writing the code explicitly so that I have some idea of what is going on. This is not a "I don't trust automation, and I'm a Real Programmer". It is a "The automation produces something that is far too simplistic to meet my needs". But if you want to do this, then the issue is that the dialog itself, rather than the caller, cares about the state, and this is a valid point. --jmn

3. To write the Save function of your own, you need to fill the dialog data members, and you use UpdateData for that. If you go to the controls, as you suggest, you bypass necessary dialog data validation as you complicate your code with a lot of control querying. Going directly to the controls is valid only in contexts in which you don't care that the dialog data doesn't reflect the current state of the controls.

I agree, if you are using DDV. Since I believe that DDV is deeply flawed in its basic design (it only validates when you click OK, and I don't like the way it works; I prefer to do continuous validation, enabling/disabling OK as the validity of the data changes, and even displaying the status of what is missing, incorrect, or inconsistent in the status line or other location. So I have deliberately chosen to avoid the DDV mechanism in favor of a far more user-friendly (in my estimation) system. Check out my essays on dialog box state maintenance and on a real-time instantly-reactive validation mechanism. If you choose to use DDV, then his point about bypassing the checking is correct. --jmn

4. You need to override OnOK, because you want to give the user the chance to save any changes. So, you write a QuerySave function, which calls Save if the user gives the OK. In general, if you override OnOK, your override must call UpdateData. In this database example, it's also appropriate to override OnCancel and have it go through the QuerySave bit, so OnCancel can also call UpdateData in some scenarios.

I also agree, if you are working in a model in which after clicking OK you ask "Save changes?" and require a confirmation. I've always worked on the model that if the user clicks OK the intent is to save any changes, and if the user didn't want to save the changes, the user would click "Cancel". If you otherwise override OnOK, you can certainly call CDialog::OnOK which implicitly calls UpdateData, since calling CDialog::OnOK eventually invokes the virtual method. --jmn

Aside: When you override OnOK, if your UpdateData call and other validation are successful, the correct way to end the dialog is to call EndDialog, usually as EndDialog(IDOK), instead of calling CDialog::OnOK. This avoids a pointless second call of UpdateData.

Yes, this is correct, if you choose to use this style. Since I choose to not use this style, it has not been a problem for me. One concern I have is that by calling EndDialog, you are stating that you know that the implementation of CDialog::OnOK is just UpdateData followed by EndDialog. This tends to violate the notion of inheritance abstraction, which bothers me. In particular, if you later subclass the dialog (another feature Microsoft doesn't support at all), then the superclass may be the class that does this, and doing an EndDialog explicitly in the subclass will bypass the superclass method and produce incorrect results. But the statement he makes is correct, at least in this version of MFC. --jmn


The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this article.
Copyright © 1999 CompanyLongName All Rights Reserved
www.flounder.com/mvp_tips.htm

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Retired
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

Comments and Discussions

 
GeneralMy vote of 5 Pin
Paul Heil2-Dec-10 11:32
Paul Heil2-Dec-10 11:32 
Generalcontrol -vs- value Pin
bkelly135-Aug-08 6:13
bkelly135-Aug-08 6:13 
GeneralOk... but what about... Pin
NGS 54967224-Apr-07 4:30
NGS 54967224-Apr-07 4:30 
GeneralRe: Ok... but what about... Pin
Joseph M. Newcomer24-Apr-07 7:20
Joseph M. Newcomer24-Apr-07 7:20 
QuestionDynamic objects... Pin
OBWANDO21-May-06 21:15
OBWANDO21-May-06 21:15 
GeneralUse UpdateWindow(hWnd) Pin
Rajesh R Subramanian20-Feb-06 22:34
professionalRajesh R Subramanian20-Feb-06 22:34 
GeneralThanks.. Pin
22491728-Feb-04 20:21
22491728-Feb-04 20:21 
QuestionHow to use "updatedata" in a thread ? Pin
penny1512-Nov-03 0:46
penny1512-Nov-03 0:46 
AnswerFairly straightforward Pin
Brian D12-Nov-03 1:27
Brian D12-Nov-03 1:27 
AnswerRe: How to use "updatedata" in a thread ? Pin
Joseph M. Newcomer16-Nov-03 11:04
Joseph M. Newcomer16-Nov-03 11:04 
AnswerRe: How to use "updatedata" in a thread ? Pin
satoufe8-Jun-04 10:25
satoufe8-Jun-04 10:25 
GeneralRe: How to use "updatedata" in a thread ? Pin
Joseph M. Newcomer8-Jun-04 17:23
Joseph M. Newcomer8-Jun-04 17:23 
GeneralData location Pin
Revon16-Oct-03 1:58
professionalRevon16-Oct-03 1:58 
Generalhelp with updating propertypages Pin
Gustav31-Jul-01 6:02
Gustav31-Jul-01 6:02 
QuestionWhat a load of .. continued Pin
Roger Onslow4-Apr-01 19:03
Roger Onslow4-Apr-01 19:03 
>I guess I'm really confused now. I even re-read my article, and nowhere do
>I say to avoid DDX.
>In fact, it is how control variables are set. I said to avoid calling
>UpdateData explicitly.

Gees .. you're a pedantic bugger Smile | :) I'm referring to the DDX mechanism ..
which includes UpdateData, DoDataExchange and various DDX_ and DDV_ calls.
If you are not calling UpdateData then you are not using the DDX mechanism
fully and are certainly not making correct use of the DDX_text etc function.
Basically you're avoiding the bulk of the mechanism and using one tiny part
(the use of DDX_Control to do subclassing of controls for you).

>I repeat here my starting assertion: Microsoft does not document the right
>way to handle controls in dialogs. Not that I've found. If I don't know
>the Right Way, it was not for want of trying to find out, six years ago.

You mustn't've tried too hard. Its very simple. And is documented. There
is even a wizard that does the work for you. You associate member variable
with controls via the dynamic data exchange mechanism. Use DDX_Text etc to
associate a data member with a control and DDX_Control to associate control
members with a control. Call UpdateData to synchronise. Gees .. that isn't
rocket science !!

>The basic place we disagree here I think is the fact that UpdateData "puts
>the code to transfer data between the controls and member variables...".
>I fail to see why I want to transfer all the data between the controls
>and the member variables all the time,

It ensures the all the member variables are in sync .. the overheads for
doing so are not high .. I think someone was once arguing about how it is
not worth inventing another way of doing something as a performance
optimisation when things are happening at the human level. The overheads of
exchanging all the data are minimal compare to human response times.
UpdateData calls will (when coded correctly) only be happening when the
users has entered or selected the final value for a control and/or is moving
on to another control. This is fairly infrequent.

The advantage of it is that you know all your member variable are current
after you UpdateData call. If you have a handler that needs to use several
values, then you'd need to explicitly put in the code to get those values
from the controls into your (member) variables. And if you forget to do so,
and just use the member variable (which can happen if you're calling nested
function) then you're using invalid data.

If it the same argument that is used for putting all the control
enabling/disabling code in the one place. BTW A very good place to put it
is in the DoDataExchange.

>particularly with the nasty side effects that come from making the
>slightest error in doing this. Mechanisms which are error-prone are a
>Bad Idea, and if someone like myself, who is very far from being a
>beginner, cannot keep the details of this straight, it is probably
>not the right mechanism.

Any mechanism is going to have to be used carefully and not is foolproof.

>Now, for those of us who are accused of being too didactic, we tend to
>question statements of the form "It's generally not a good idea for a
>dialog to handle EN_CHANGE". Are you doing what you accused me of?

I made a general statement .. I didn't save "using EN_CHANGE with a control
is flawed, its not well documents. You should never use it. ever".

I said generally it is not a good idea for the dialog to handle it .. it is
better handled in an CEdit derived class using message reflection.

Also (especially in modal dialogs) one usually doesn't want things to happen
on every keystroke as you type in .. unless you are doing individual control
validation. In that case, either derive a CEdit class to do it, or handle
it in the dialog by using an edit control variable.

NOTE: I never said DDX/UpdateData/DoDataExchange will handle every
situations .. its horses for courses. Simply keeping control data in sync
with member vars is a nice little job that the ddx mechanism does very well.
And it provides for direct access to controls (eg for custom validation) by
providing a DDX_Control function in its implementation.

>Why shouldn't a dialog handle EN_CHANGE? The EN_CHANGE is a notification
>to the dialog that the contents of the control have changed, and therefore
>it is appropriate to have the notification go to the dialog.

Why do you think control reflection was added to MFC .. because you get
better design from having the control handle most of its messages.

>The control itself has no idea what context it is in and consequently
>cannot determine that it is now time to re-evaluate the state of other
>controls in the dialog.

The control shouldn't need to know .. if it does need to know, it can ask
the dialog for the information it needs.

And my statement was an "in general" .. not never all always .. if one finds
a situation where EN_CHANGE is better handled by the dialog itself, then
that is a situation where EN_CHANGE is better handled by the dialog itself.
It doesn't invalidate my remark.

One should be careful when using UpdateData in a EN_CHANGE/EN_UPDATE context
as UpdateData is really designed for working with the finished results
(selection actually made from a listbox, the edited text in an edit box
etc). If one wants to stick ones nose into what is happening while the user
is typing, or mousing thru a listbox, then do not use UpdateData. But it is
not good advice to say never use UpdateData in a modal dialog.

>The reason I need to restore the state of the controls should be obvious:
>having made changes in the state of the controls as a consequence of some
>event, I need to get that state back into the controls. The DDX mechanism
>(for values) makes a naive assumption that the only possible changes in
>controls are caused by users, and doesn't terribly well support the notion
>that controls need to have effects on each other, sometimes in complex
ways.

Why do you think that? When using UpdateData in your code, you simply work
with the data values and rely on the ddx mechanism to get the data to and
from the controls. It doesn't matter if the data is related in complex ways
.. as long as the data and controls are in sync. You just do whatever needs
to be done to the data and then resync with UpdateData.

If you put your control state changing code (eg EnableWindow etc calls) in
your DoDataExchange (this is probably the best place for them if you are
using ddx), then the UpdateData code will update both the value an the
state.


AnswerRe: What a load of .. continued second part Pin
Roger Onslow4-Apr-01 19:04
Roger Onslow4-Apr-01 19:04 
QuestionWhat a load of .. Pin
Roger Onslow3-Apr-01 21:28
Roger Onslow3-Apr-01 21:28 
AnswerRe: What a load of .. Pin
Joseph M. Newcomer3-Apr-01 22:13
Joseph M. Newcomer3-Apr-01 22:13 
GeneralRe: What a load of .. Pin
Roger Onslow3-Apr-01 22:39
Roger Onslow3-Apr-01 22:39 
GeneralRe: What a load of .. Pin
Joseph M. Newcomer4-Apr-01 16:38
Joseph M. Newcomer4-Apr-01 16:38 
GeneralRe: What a load of .. Pin
Roger Onslow3-Apr-01 23:04
Roger Onslow3-Apr-01 23:04 
GeneralRe: What a load of .. Pin
4-Apr-01 1:46
suss4-Apr-01 1:46 
GeneralRe: What a load of .. Pin
Peter Donahue3-Aug-01 11:05
Peter Donahue3-Aug-01 11:05 
GeneralRe: What a load of .. Pin
Joseph M. Newcomer3-Aug-01 15:13
Joseph M. Newcomer3-Aug-01 15:13 
Generalif (IDOK == dlg.DoModal()) {assign variables} Pin
2-Apr-01 14:46
suss2-Apr-01 14:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.