Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

combo boxes

11 views
Skip to first unread message

Henry

unread,
Feb 29, 2004, 10:39:57 AM2/29/04
to
Also, I just can't get my headr round combo boxes. I've always managed to
get it by faffing with the code, but every time I add a

OnSelChangeCombo()

function, I can never work out how it works, the string varialble attached
always seems to be one step behind, if you know what I mean?

e.g.

void mydlg::OnSelChangeCombo()
{
MessageBox(m_sCombo);
}

Gives me NOT the item selected, but the item preivous displayed.
I've always maanged to get it right, by faffing lots with
UpdateData(TRUE)/FALSE, and but right now, I can't seem to get it right!!

Can anyone shed any light? interms of its logic? I can never get it right
first time'cause I don't wquite know what it does, and usually, I eventually
stumble along a solution that works.

Thanks


Jeff Partch [MVP]

unread,
Feb 29, 2004, 11:56:02 AM2/29/04
to
"Henry" <abs...@bath.ac.uk> wrote in message news:HturI...@bath.ac.uk...

So is m_sCombo a CString mapped to the ComboBox using DDX_CBString? If so,
the documentation...

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_ddx_cbstring.asp

...says that it, "...manages the transfer of CString data between the edit
control of a combo box control ... and a CString data member...", but it
seems to use GetWindowText to do it, and which according to the
documentation...

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/getwindowtext.asp

"...If the target window is owned by the current process, GetWindowText
causes a WM_GETTEXT message to be sent to the specified window or
control...", and according to the WM_GETTEXT documentation...

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Windows/WindowReference/WindowMessages/WM_GETTEXT.asp

"...For a combo box, the text is the content of the edit control (or
static-text) portion of the combo box", but according to this kb article...

http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B66365

"When an application receives the CBN_SELCHANGE notification message, the
edit/static portion of the combo box has not been updated". While it refers
to GetDlgItemText, the documentation of that API...

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/dialogboxes/dialogboxreference/dialogboxfunctions/getdlgitemtext.asp

...says that "The GetDlgItemText function sends a WM_GETTEXT message to the
control". So it seems like the same situation to me. See if you can use
GetCurSel followed by GetLBText to solve this one.

--
Jeff Partch [VC++ MVP]


Joseph M. Newcomer

unread,
Feb 29, 2004, 5:12:26 PM2/29/04
to
The first thing to do is forget the concept of string variables attached to controls. This
means you are using UpdateData, which I consider a fundamental design error. So it doesn't
surprise me in the slightest that there is an error. In fact, the code shown is guaranteed
to be incorrect, because it makes no effort to keep the two states in sync (and there
should be no reason to keep two copies of something in sync, when you can avoid the
problem entirely by never requiring two copies exist at all)

Use GetWindowText to get the current text. This should solve the problem.

OnSelChangeCombo:

CString s;
c_Combo.GetWindowText(s);
MessageBox(s);

Bottom line: if you have two copies of a value, one of them will be wrong.

Read my essays on dialog boxes on my MVP Tips site.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Jeff Partch [MVP]

unread,
Feb 29, 2004, 6:11:14 PM2/29/04
to
"Joseph M. Newcomer" <newc...@flounder.com> wrote in message
news:2oo440hgtf9v4cg1f...@4ax.com...

> Use GetWindowText to get the current text. This should solve the problem.
>
> OnSelChangeCombo:
>
> CString s;
> c_Combo.GetWindowText(s);
> MessageBox(s);

Wouldn't the issues I addressed in my reply to the OP some 5 hours ago lead
you to at least question the usefulness of this approach? Remember that one
of the OPs problems was that, "the string varialble attached always seems to
be one step behind". Or is this your way of saying that kb article Q66365 is
incorrect? Or is it that you just don't give a rat's whiskers what anyone
else has to say?

Joseph M. Newcomer

unread,
Mar 1, 2004, 12:04:38 AM3/1/04
to
Mostly it is the case that every time someone has problems with UpdateData, the problems
arise because there is a synchronization problem between the two copies. It becomes
critical for the programmer to always know what state things are in, and deal with keeping
the two copies in sync. Life becomes so much simpler when there is only one copy of the
data. It is the problem of cache management; only if cache management is completely
transparent is it successful. This is why caching works in hardware. Only in really
obscure cases, such as dealing with very low-level hardware interfaces, does the caching
manifest itself, and in that case there is a simple formulaic approach that always works.

I started using UpdateData because it seemed to be the "right thing". After a couple
months of dealing with keeping the controls and values synchronized, which is nontrivial
in most cases, I reverted back to letting each control define its value. WIthout
exception, every program that has passed over my desk for the last decade which used
UpdateData used it incorrectly, and often what I was being asked to fix was "this dialog
doesn't work right". A day of editing all the dialogs to remove UpdateData and not only
did they work, but they became maintainable (most of the bugs I saw were the result of
adding a new control to the dialog). One of the key principles of creating maintainable
code is to maintain orthogonality; local changes should have local impact and not global
impact. UpdateData violates that principle of parsimony. A failure to understand the
entire complex of UpdateData calls and the relation of every control and every variable to
each UpdateData results in code that fails in odd ways.

This is not a whimsical attitude on my part; it is the result of observing a LOT of bad
programs. By "bad programs" I don't mean programs that violate my aesthetic; I mean
programs that don't work correctly. And the central cause of the failures was a failure to
understand the complete set of relationships of the variables and controls.

Once you except the trivial cases of a dialog with a simple edit control and OK/Cancel
buttons, dialogs need to handle complex interactions of the controls. UpdateData makes
this hard; for example, it becomes necsssary to introduce a THIRD copy of a variable in
many cases (I've seen it done far too often) so the control values can be "snapped back"
to their corresponding variables, then the new value is set, then the values are "snapped
out" to the controls. And the worst part of it is that some controls, such as list boxes,
combo boxes, and radiobuttons, don't even get this part right.

My impression of UpdateData was that it was thought of as a cute idea and enshrined as a
mehodology without anyone thinking through the implications. Certainly a lot of
highly-experienced programmers get it wrong. Anything that beginners get consistently
wrong and experienced programmers make serious mistakes with is a mechanism that needs to
be carefully thought about. I'm the voice that says "rethink what you are doing". There
are alternative mechanisms, many of which are far more usable than UpdateData; they always
work, they always do the "right thing", and they never, ever generate newsgroup questions
because they are simple and easy to use, orthogonal, and result in code that is actually
easy to maintain.

As far as seeing your post, note that I download a bunch of stuff, then as I need
diversion I answer various posts. During the several hours between downloads, other
answers get put up. I don't see those until I do the next download.
joe

Joseph M. Newcomer [MVP]

Jase

unread,
Mar 1, 2004, 1:23:05 AM3/1/04
to
"Henry" <abs...@bath.ac.uk> wrote in message news:HturI...@bath.ac.uk...

Call UpdateData(TRUE) as the first function call in your OnSelChangeCombo
handler.

Jase


Henry

unread,
Mar 1, 2004, 3:15:10 AM3/1/04
to
> >
>
> Call UpdateData(TRUE) as the first function call in your OnSelChangeCombo
> handler.
>

This doesn't seem to work.

For some peculiar reaons, the UpdateData(TRUE) seems to be called before the
actually Select change procress., thus if ITEM1 is the current item, and you
wanna select ITEM2, the UpdateData(TRUE) is called before you've selected
ITEM2, thus reuslting in the user not be able to change the selection at
all.

However, with listboxes, there doesn't seem to be such a problem, just combo
boxes, I think...

I think I've always managed to solve it by using the Killfocus instead to
ensure that the update occurs at the correct place. How tedious is that!!


Henry

unread,
Mar 1, 2004, 3:22:00 AM3/1/04
to
Oh yes I remember now, just checked my code agian.

UpdateData(TRUE) does work if it's a "droplist", i.e. no user keyboard input
allowed.

If the combobox is a "dropdown", where user keyboard is permmited, thet
UpdataDATA(TRUE) doesn't work. The data get updated at the wrong time, so to
speak.

It's all very peculiar. Thus for "dropdowns", I resorted to using
Kililfocus.


"Jase" <jshe...@spamblock.enersol.com.au> wrote in message
news:4042d6ca$0$25183$afc3...@news.optusnet.com.au...

Doug Harrison [MVP]

unread,
Mar 1, 2004, 10:38:33 AM3/1/04
to
Joseph M. Newcomer wrote:

>As far as seeing your post, note that I download a bunch of stuff, then as I need
>diversion I answer various posts. During the several hours between downloads, other
>answers get put up. I don't see those until I do the next download.

As you clearly are replying to these messages contemporaneously as you read
them, maybe it would be reasonable to download new headers before you embark
on a read-and-answer session. It's what I do when there's a long delay
between the time I download headers and read messages, because I don't want
to waste my time answering a question that's already been adequately
answered. I also like to consider other replies before I add my $.02.
Sometimes, the other respondents have thought of things I've overlooked and
may have answered better than I would have. Sometimes, I think of things
others have overlooked, and I can reply more effectively if I consider
what's already been said.

--
Doug Harrison
Microsoft MVP - Visual C++

Doug Harrison [MVP]

unread,
Mar 1, 2004, 10:38:32 AM3/1/04
to
Henry wrote:

>> Call UpdateData(TRUE) as the first function call in your OnSelChangeCombo
>> handler.
>>
>
>This doesn't seem to work.

Even if you can get it to work, it's the wrong approach. For an explanation
on the proper care and feeding of UpdateData, see this message:

http://groups.google.com/groups?selm=2gadivke7i40q160u450tg2fprda23besl%404ax.com

>For some peculiar reaons, the UpdateData(TRUE) seems to be called before the
>actually Select change procress., thus if ITEM1 is the current item, and you
>wanna select ITEM2, the UpdateData(TRUE) is called before you've selected
>ITEM2, thus reuslting in the user not be able to change the selection at
>all.
>
>However, with listboxes, there doesn't seem to be such a problem, just combo
>boxes, I think...
>
>I think I've always managed to solve it by using the Killfocus instead to
>ensure that the update occurs at the correct place. How tedious is that!!

Doing UpdateData in response to WM_KILLFOCUS is wrong. Only use UpdateData
to transfer and validate _all_ variables participating in DDX/DDV. Doing it
in WM_KILLFOCUS could point out a validation problem with an arbitrary
control, ultimately setting the focus to it with SetFocus, which is a bad
idea in a WM_KILLFOCUS handler. In addition, validating on WM_KILLFOCUS can
create the annoying phenomenon in which you can't leave a control unless it
passes validation, not even to visit another control, copy some text, and
return to the first control to make it valid.

As for your problem, see Jeff's message for the proper solution. Inside a
CBN_SELCHANGE handler, the editbox part tends to contain stale data, which
doesn't reflect what's currently selected in the listbox part. Thus you need
to get the current selection with GetCurSel and pass the returned index to
GetLBText to get the text of the currently selected item.

Doug Harrison [MVP]

unread,
Mar 1, 2004, 10:38:31 AM3/1/04
to
Jase wrote:

>Call UpdateData(TRUE) as the first function call in your OnSelChangeCombo
>handler.

No! Do not call UpdateData unless you want to transfer and validate all
member variables participating in DDX/DDV. See this message for more:

http://groups.google.com/groups?selm=2gadivke7i40q160u450tg2fprda23besl%404ax.com

Balboos

unread,
Mar 1, 2004, 6:36:07 PM3/1/04
to
Two Cents;

I've written a commercial (still on sale) MFC dialog based applicaton.
In it's 60,000+ lines, there's not a single call to UpdateData().

In all cases, I either use the Get[Set]DlgItem...() methods. I found
the UpdateData() a royal pain - maybe because, as a 'C' "Old Timer", I
have a need for control. This is lost if the data calls the
(potentially) massive Data Exchange methods, which are too much "all or
nothing" for my taste.

Balboos

Jase

unread,
Mar 1, 2004, 7:45:18 PM3/1/04
to
Quite right. UpdateData doesn't work for Dropdown lists. You learn something
new every day ;-). I guess you'll have to heed the advice of our anal
friends, and do it another way, cos that way is WRONG MAN ;-).

Jase

"Henry" <abs...@bath.ac.uk> wrote in message news:Htw1...@bath.ac.uk...

Doug Harrison [MVP]

unread,
Mar 1, 2004, 11:38:37 PM3/1/04
to
Balboos wrote:

>Two Cents;
>
>I've written a commercial (still on sale) MFC dialog based applicaton.
>In it's 60,000+ lines, there's not a single call to UpdateData().

I'd bet there are many MFC functions you don't call. Of course, UpdateData
is being called on your behalf every time OnInitDialog and OnOK execute.

>In all cases, I either use the Get[Set]DlgItem...() methods. I found
>the UpdateData() a royal pain - maybe because, as a 'C' "Old Timer", I
>have a need for control. This is lost if the data calls the
>(potentially) massive Data Exchange methods, which are too much "all or
>nothing" for my taste.

Of course, you shouldn't use UpdateData except to transfer and validate all
the controls participating in DDX/DDV. I talked about that here:

http://groups.google.com/groups?selm=2gadivke7i40q160u450tg2fprda23besl%404ax.com

One time to explicitly call UpdateData is when overriding OnOK. I described
how to do that here:

http://groups.google.com/groups?selm=9v9g10t8t6c4jgs52t2ulfdamirui6g6kb%404ax.com

Balboos

unread,
Mar 2, 2004, 7:00:47 AM3/2/04
to
MFC base applications do, behind the scenes, what they will do.
Generally beyond, or not worth, my efforts to control. Even if it calls
UpdateData() for me in OnOK() - it has no real effect. By then, I've
handled my data: and none of it was tied to data exchange.

In a sense, you agree with me - "of course, don't call UpdateData(...".
But when does one want to access all of the controls at once?

One might as well do it themselves, anyway. The Data Exchange call is
calling functions, one per 'member', and so would I. Except, I'd do it
(1) only as needed, (2) only to as many as actually need to be called.

I can see a scenario when it would have been useful. When PC's were
slow, compared to the UI requirements, it would enable a more
simultaneous looking refresh of screen data. Perhaps the original idea?

UpdateData() is, "I feel", used deliberately only until one gains enough
experience to know better.

But then: there's more then one way to 'skin a cat' or 'code an
algorithm'. Just like some of us make a lot of use of GetDlgItem(),
coding to take advantage of it, while others recommend against it use.

Balboos

Doug Harrison [MVP]

unread,
Mar 2, 2004, 1:11:17 PM3/2/04
to
Balboos wrote:

>MFC base applications do, behind the scenes, what they will do.
>Generally beyond, or not worth, my efforts to control. Even if it calls
>UpdateData() for me in OnOK() - it has no real effect. By then, I've
>handled my data: and none of it was tied to data exchange.
>
>In a sense, you agree with me - "of course, don't call UpdateData(...".

Please, quote accurately. :) "Of course, you shouldn't use UpdateData except


to transfer and validate all the controls participating in DDX/DDV."

> But when does one want to access all of the controls at once?

When implementing an OnOK override. Being able to implement OnOK in a
straightforward, consistent way is a very important application.

When implementing a "Save" feature, which can save current settings to a
named file or other persistent storage.

While I sometimes wish the IDE wizards packaged dialog data into a separate
struct, and as I mentioned in a message I've linked to, I wish controls
supported fine-grained transfer and validation, the overall paradigm is
sound. It extends nicely to dialogs composed out of top-level controls and
one or more subdialogs. You just ask the subdialogs to UpdateData. While I'm
fully aware of and have explained the limitations of DDX/DDV, what I don't
get is why anyone would think you wouldn't need something comparable to
UpdateData in any general purpose approach to the problem.

>One might as well do it themselves, anyway. The Data Exchange call is
>calling functions, one per 'member', and so would I. Except, I'd do it
>(1) only as needed, (2) only to as many as actually need to be called.

And in every dialog box, you'd try to employ more or less the same ad hoc
method. You might do all your control querying and validation in OnOK. But
then you might want to make this a subdialog, or implement the "Save"
feature. Then it would be time to redesign, and move things out of OnOK,
into a function I'll leave to your imagination. :)

>I can see a scenario when it would have been useful. When PC's were
>slow, compared to the UI requirements, it would enable a more
>simultaneous looking refresh of screen data. Perhaps the original idea?

I don't think it has anything to with that. The idea was to provide an easy
way to map control contents to C++ types like BOOLs and CStrings, with
automatic transfer and validation.

>UpdateData() is, "I feel", used deliberately only until one gains enough
>experience to know better.

Beyond "feeling" that way, I don't think that's a supportable position. The
function has valid uses, and it's good to understand what they are.

>But then: there's more then one way to 'skin a cat' or 'code an
>algorithm'. Just like some of us make a lot of use of GetDlgItem(),
>coding to take advantage of it, while others recommend against it use.

There are genuine reasons to prefer control variables to GetDlgItem and
define them even if you're going to use GetDlgItem, such as in a loop over
control IDs. See the second half of this message for details:

http://groups.google.com/groups?selm=v49g3vo3ddrrpsmmltnhk5v5du4i6hn8do%404ax.com

0 new messages