Date: | April 19, 2004 / year-entry #149 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040419-00/?p=39753 |
Comments: | 31 |
Summary: | "I'll do my field validation when I get a WM_KILLFOCUS message." This is wrong for multiple reasons. First, you may not get your focus loss message until it's too late. Consider a dialog box with an edit control and an OK button. The edit control validates its contents on receipt of the WM_KILLFOCUS message. Suppose... |
"I'll do my field validation when I get a WM_KILLFOCUS message." This is wrong for multiple reasons. First, you may not get your focus loss message until it's too late. Consider a dialog box with an edit control and an OK button. The edit control validates its contents on receipt of the WM_KILLFOCUS message. Suppose the user fills in some invalid data. Under favorable circumstances, the user clicks the OK button. Clicking the OK button causes focus to move away from the edit control, so the edit control's WM_KILLFOCUS runs and gets a chance to tell the user that the field is no good. Since button clicks do not fire until the mouse is released while still over the button, invalid data will pop up a message box, which steals focus, and now the mouse-button-release doesn't go to the button control. Result: Error message and IDOK action does not execute. Now let's consider less favorable circumstances. Instead of clicking on the OK button, the user just presses Enter or types the keyboard accelerator for whatever button dismisses the dialog. The accelerator is converted by IsDialogMessage into a WM_COMMAND with the button control ID. Focus does not change. So now the IDOK (or whatever) handler runs and calls EndDialog() or performs whatever action the button represents. If the dialog exits, then focus will leave the edit control as part of dialog box destruction, and only then will the validation occur, but it's too late now. The dialog is already exiting. Alternatively, if the action in response to the button is not dialog termination but rather starting some other procedure, then it will do it based on the unvalidated data in the dialog box, which is likely not what you want. Only when that procedure moves focus (say, by displaying a progress dialog) will the edit control receive a WM_KILLFOCUS, at which time it is too late to do anything. The procedure (using the unvalidated data) is already under way. There is also a usability problem with validating on focus loss. Suppose the user starts typing data into the edit control, and then the user gets distracted. Maybe they need to open a piece of email that has the information they need. Maybe they got a phone call and need to look up something in their Contacts database. Maybe they went to the bathroom and the screen saver just kicked in. The user does not want a "Sorry, that partial information you entered is invalid" error dialog, because they aren't yet finished entering the data. I've told you all the places you shouldn't do validation but haven't said where you should. Do the validation when the users indicate that they are done with data entry and want to go on to the next step. For a simple dialog, this would mean performing validation when the OK or other action verb button is clicked. For a wizard, it would be when the Next button is clicked. For a tabbed dialog, it would be when the user tabs to a new page. (Warnings that do not change focus are permitted, like the balloon tip that apperas if you accidentally turn on Caps Lock while typing your password.) |
Comments (31)
Comments are closed. |
My first project in the real world was at what had been a C & Unix shop and was now being brought kicking and screaming into the land of C++ & Windows along with MFC. We did validation on WM_KILLFOCUS because (I think) the customer wanted instant validation like they had in their old applications on a DG mainframe and we didn’t know how better to do it. I think we did validation in DoDataExchange even if the user pressed Return, but I’m not certain.
You didn’t mention the worst problem, which I encountered a lot, which is that having reprimanded the user and set focus back to the invalid control, the program gets WM_KILLFOCUS for the control that temporarily gained focus and tries to validate that too. So we could sometimes end up presenting the user with an infinite series of alternating error dialogs.
Somehow I actually managed to learn C++, Windows and MFC during this project and got the hang of making dialogs just about usable. So I got the task of clearing up dozens of defect reports regarding the horrible dialogs we had inflicted on the customer in the first delivery. I also got the task of writing a bunch of ActiveX controls. This is why I have tried to avoid GUI programming ever since.
"For a tabbed dialog, it would be when the user tabs to a new page. "
No it bloody wouldn’t. In practice that’s no less annoying than focus change validation, for essentially the same reasons. Oftentimes (especially when trying to get a feel for an application and evaluate its knobbage) I just want to click around a tabbed dialogue box to see what’s there. This lets me get an idea of the kind of information that the box needs and the kind of thing it lets me do. Since blank fields are normally invalid fields, tab change validation prevents this. It’s immensely frustrating. Switching tabs is not an action that demands input validation.
You’re right. The context of the original problem that inspired this entry was a wizard, in which case page navigation = clicking "Next". My mistake for rewriting the article to be about tabbed dialogs without changing everything that needed to be changed.
I suppose Windows.Forms implements validation in somewhat similar BAD way.. Changing focus goes through all sorts of ActivateControl methods (see call stack) and ends in Validating event handler. If dialog is closed with X-button, controls are not validated. And so on… This is as of Framework 1.1
if a dialog is closed with the X button, validation SHOULDN’T take place — the close button is effectively the same as "cancel" throughout the windows u/i, so you shouldn’t make a dialog box that saves its values when you hit the close button.
The worst way to do validation is when someone tries to validate on change. That is, an edit box with a spin control, designed for entry of signed integers, which, when any character is entered, validates the new contents. This way, it is absolutely impossible to intuitively enter a negative number (you have to first enter the absolute value, then add a minus, because "-" is not a valid integer) and you cannot clear the box because "" is not a valid integer. And, to further complicate the issue, they sometimes replace the invalid text with valid text (usually 0 or some other default value). See the Color common dialog, which replaces invalid values with either 0 or 255.
There are also number edit controls that aren’t (only the spin control actually change the contents; the edit is read-only — the year entry subcontrol of the common calendar), file name edit controls that aren’t (the edit is read-only, and the only way to select a file is through the Browse button) and especially directory name combo boxes that aren’t (the combo is in history list mode, not allowing free text entry, and there is a Browse button that opens a Browse for directory dialog that also has no means to enter text directly or paste a path from the clipboard). An example of the latter is the Get Pictures from Scanner wizard in Windows XP.
There are other examples of non-usable controls, and many of them are caused by developers being overprotective. Let users enter all they want and defer validation to the time they click OK or Next or Apply, then warn them with “-42.7 is not a valid integer”, “A file named foo.txt does not exist” and the like.
Oh, and the last one, the autocomplete file name edit control, as used in the File Open/Save common dialog. For an Open dialog, it’s logical to autocomplete a name, since the user usually wants to open an existing file. Unfortunately, in a Save dialog a string that happens to be a prefix of an existing filename is also autocompleted. So the user presses Alt+F A, quickly enters a name and presses Enter to save the file, but the name gets autocompleted and the dialog gives an overwrite warning. Or, in the unfortunate case if the developer has disabled overwrite warnings, the file gets overwritten. Oops, too bad. Be careful next time.
? If you just hit Enter in the File Save dialog then autocomplete does not complete your partial name. You have to hit the DownArrow to activate the autocomplete suggestion.
WM_KILLFOCUS is the wrong time to do field validation….
I absolutely LOVE validation with the tooltip "talk box." The Caps lock warning is wonderful, as are the bubbles that come up saying "files can’t contain these letters" and the like. I love it because it points you exactly where the error is and it lets you correct it before hitting OK. Or even if it comes up after you hit OK, the pointing at the actual data at fault is so much better than hunting down the offending value.
"If you just hit Enter in the File Save dialog then autocomplete does not complete your partial name".
Not if use "Use inline AutoComplete" in Internet Options->Advanced.
One of the many reasons why that is off by default.
I use Opera, so I see a lot of places where JavaScript just breaks. Lost focus ones where the validation code is broken, but it still shoves you back into the field, is just dreadful.
While I agree that validations should be performed at posting time, I don’t see a reason why validations shouldn’t also be performed on lost focus. Do not pop up a modal dialog, do not force focus back there, but do something visual: turn the field a different color, add a tray tip, or whatever. Realtime validations are nice, but they just shouldn’t be so forceful.
It does require coding your validations so the same code can validate the entire form, one field, or some semantically linked set of fields. Tricky, but often possible.
And, of course, there are usually backend ways to trick the validator. Edit an ini file or registry key that underlies one of these fields, and you can often enter out of bounds data that still works okay. Or hey, just block all the WM_KILLFOCUS messages. ;-)
And how about validations on WM_SETFOCUS?
I am not kidding or teasing here. Let’s put aside user experience
for a moment and just be technical. I have triggers for validations
on field entry and exit and on every field content change, plus final
validations for OK/Cancel buttons.
Exit validation is triggered on TAB or WM_SETFOCUS if the field
getting focus is not the same as field being current up until that
point. What I am handling here is button click inside another
edit field.
I don’t handle field changes on MOUSE messages because I
don’t want to interfere with clicks I am supposed to pass
to standard edit control’s WndProc. I just felt that this could
be a problem for contextual menus and so far it seemed like
WM_SETFOCUS was good choice.
I’d be glad if Raymond can point me to a better solution.
From UI and user experience points of view, well I am careful what
I put into these validations, but sometimes it’s the best place to
warn user about his entry, if not with a dialog, but with some
message in status bar or a sound.
Robust Validation:
1. If the user stops typing for 15 to 30 seconds, validate their input, and show a bubble with advice on what’s wrong with their input.
2. If the user tabs away from a control, and the input is invalid, validate, and show a bubble with advice on what’s wrong with their input.
3. When the user does a commit on the displayed dialog (OK button or otherwise), do a final validation. If this validation fails, show the bubble over the control, set the focus to the control, and allow the user to change the details.
Sure, this is a lot of work, but it’s worth it. It’s not as good as it could be though.
When I worked at MS, one proposal I had (which apparently didn’t get off the drawing board) was for several upgrades to the edit control. One of these would have been this:
Allow Word-style underlined squiggles to operate in edit boxes on dialogs. That way, if the text in the box is invalid, the user gets immediate and unobtrusive feedback (the red underline with a tooltip explaining what’s wrong if they hover over it). It also means that if there’s more than one error, you don’t need (say) 8 tooltip bubbles floating over the UI.
I had several other ideas on the same lines… but alas, they never got implemented.
Squigglies are way beyond the scope of the basic edit control.
I can’t remember the number of times I had to tell newbie HTML developers that this was a horrible abuse of scripting in wet pages. But they all seem to want to do it for their web forms…
While we’re on the subject, not only can you not trust KILLFOCUS, you can’t trust that "disabled" or "hidden" controls will not contain input when being used by malicious end users. Search the web for "shatter attacks" if you want to know why.
Only really necessary when the user has less privilege than the thing accepting the data. Eg, a server blindly accepting input from a field it "thought" was read-only (other why are you accepting it if it was read-only?) or a GUI app running at a higher privilege level then the user (such as SYSTEM… don’t do that!!!).
But you could end up with goofy data even if it doesn’t present a real security risk. Never trust the client (and the client should never trust the server)!
Raymond:
Sure… they’d be out of scope for the basic control, but would be a good candidate to be added into something along the lines of comctl.
They’d also be a great UI addition – most dialogs would probably use them.
Still… I can dream :)
How is it out of scope for the basic edit control? Autocomplete is there for the basic edit controls? What makes squiggles( err, what is it err.. text services) out of scope? Seems like it would eliminate a lot of poorly typed posts and irritating web page script code. Still doesn’t solve web page validation, but that’s a different issue.
If you can bolt on autocomplete to comboboxes edit control, why not bolt on autospell check to edit boxes via some kind of property from script. Just my 2¢ post yet again.
The auto complete in the save dialog is the of the most annoying Windows "features" I have come across. I knew there must be an option to turn it off as it always happened on my old work PC but never occurred on my home PC (both running the same version of Windows). I searched through all the Explorer options for a way of turning it off, then I searched through all of those tedious system policy options looking for where the option could be changed.
Finally (thanks to Eyal), I have found out where it is set. The options for the common file dialogs are under "Internet Options" in Internet Explorer. How silly I am for not thinking to look there.
Jonathan
Autocomplete is not a feature of the basic edit control. It’s an add-on (SHAutoComplete). And notice that autocomplete takes place outside the edit control; the edit control itself has no idea that autocomplete is happening to it.
Whereas if you want squigglies, now you have to change the way the edit control draws text, you need new messages to control which parts of the edit control get squiggled.
There was another thread about why spell checking is not an OS feature. I’ll refer you to that one.
"The worst way to do validation is when someone tries to validate on change. That is, an edit box with a spin control, designed for entry of signed integers, which, when any character is entered, validates the new contents."
It can get even worse than that. The most annoying piece of input validation I’ve seen was in Canon’s PhotoRecord photo printing program. Their Print dialog had the typical option to print pages M through N with separate edit boxes for M and N.
But, they validated on WM_KILLFOCUS and cross checked these two fields against each other. And they remembered the entries in these two fields from the last time you used the dialog.
I would often want to print, say, pages 3-5 and 8-9 from an album. Since they didn’t have an Office-style page selection field where I could just enter "3-5,8-9", I had to first print pages 3 through 5, and then use a second Print command to print pages 8 and 9.
So I’d enter 8 in the first field and hit Tab, and because 8 through 5 is not a valid range they would "correct" my page 8 to be 5 instead. I would have to change the last-page field to 9 and then come back to enter 8 for the first page again.
Just to add insult to injury, their wacky validation code also disabled the normal behavior of Tab that selects an entire edit field when you tab to it. Instead, it put the caret at the beginning of the field with nothing selected.
To their credit, Canon did fix this in version 2 of PhotoRecord by using a completely different UI to select pages for printing.
So what it the right way to perform validation in a modeless properties editor? I took a look at the property editor within VS.NET. When I enter invalid text into an edit control and transfer focus to anything – I get a dialog popped up and focus is reset to the control. Having a look at the messages being sent validation is occuring on WM_KILLFOCUS – just as I would have expected…
You can do unobtrusive validation on focus loss but you shouldn’t interrupt the user’s flow. When the changes are applied, then you can put up whatever error messages you want.
In test #2 above, I left out one step. After running the app, open the About box. Then type some text in the edit controls and tab around the dialog. (I know, that’s obvious–but I always hate it when I describe a test procedure and forget a step. <g>)
The behavior Michael describes is standard behavior for IsDialogMessage for any control that reports DLGC_HASSETSEL. So it’s not "just" tab behavior and not "just" edit behavior; it’s a combination of both.
Michael Geary wrote:
> Just to add insult to injury, their wacky
> validation code also disabled the normal
> behavior of Tab that selects an entire edit
> field when you tab to it. Instead, it put
> the caret at the beginning of the field with
> nothing selected
Err… what you described as normal tab behavior isn’t normal Tab behavior. What they were doing, however, *is* normal, unadulterated, raw EDIT control tab behavior.
Sorry, Simon, but in a normal Windows dialog, tabbing into an Edit control *does* select the text.
Try either of these two tests on Windows XP:
1) Run Notepad. Open the Replace dialog. Enter some text into both edit boxes. Hit Tab a few times.
2) Use VS.NET 2003 to create a vanilla Win32 application. Use the dialog editor to add two Edit controls to the About box. Compile and run the app. Type some text into the Edit controls. Hit Tab a few times.
You’ll see that in both cases, the Tab key selects the text in the edit controls.
Thanks, Raymond. That DLGC_HASSETSEL trick is a good example of why programs should use IsDialogMessage instead of rolling their own tab key management as I’ve seen some apps do (maybe that is what tripped up the print dialog in PhotoRecord).
Peter Torr notes: ‘While we’re on the subject, not only can you not trust KILLFOCUS, you can’t trust that "disabled" or "hidden" controls will not contain input when being used by malicious end users. Search the web for "shatter attacks" if you want to know why.’
Oh, I love those web sites that force you through a EULA in a readonly [textarea].
If they had just put it on the page, then I would have had to accept it. But nooo… it’s a textarea. My standard Proxomitron filters remove readonly and disabled flags. I don’t think I’ve accepted the actual text of an online EULA in years.
Hadn’t heard about shatter attacks, though. Ah, some good lunchtime reading!
Commenting on this entry has been closed.
It gums up the focus system.