Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
These two events are just about the only carryovers from FoxPro 2.x’s READ that are still useful. When fires when a control is offered focus. If When returns .T., the control can have the focus and its GotFocus event fires. (When also fires at a few other times—see below.) A good rule of thumb is for textual controls (text box, edit box, spinner), Valid fires whenever the control loses focus and determines whether it’s allowed to lose focus. If so, the control’s LostFocus fires. For non-textual controls, Valid fires when the user does something that could change the control’s Value.
PROCEDURE oObject.When | oObject.Valid
[ LPARAMETERS nIndex ]
The nIndex parameter is passed when oObject is a control array. That's as it should be. What isn't as it should be is that, when some types of controls are placed in a control array, the When and Valid for the control array fire only if a procedure actually exists for the same event in the class definition for the control. That's right—it's not enough to define When and Valid for the control array. At least some of the time, you have to do so for the types of objects in the array, too, even though that code never executes. (ErrorMessage and Message have the same weird behavior. We haven't tested every type of control in this scenario, but the problem occurs at least for text boxes, edit boxes, spinners and check boxes.) |
When is pretty simple. It fires when you head for the object. There are only a few surprises. First, the When for a list box fires every time you move the highlight in the list. This behavior is the same as that of lists in 2.x and is very handy. It lets you do something, like update other controls, as the user moves through the list.
Second, the When for some controls fires more than once when you use the control. When you use a button (command or option), check box, or combo, if focus will still be on that control afterwards, the When fires following the Valid. If you click on the control, you get the following sequence: When, GotFocus, Valid, When.
Finally, ActiveControl doesn’t point to the control whose When you’re executing. The control doesn’t become ActiveControl until When returns .T. and GotFocus fires.
Valid is trickier. It can return either a logical or a numeric value. Many controls ignore most of the return values. Only textual controls (text box, edit box, spinner and combo box with Style = 0) pay attention to a return value of .F. or 0. For other controls, despite Help to the contrary, no matter what you return, focus is permitted to leave the object. Since the other controls present only valid options in the first place, this is appropriate behavior—it's just documented wrong. |
Returning a non-zero value is even trickier. Lists and option buttons totally ignore numeric returns, as do controls in a grid. Other controls do move the focus forward or backward the specified amount. However, we strongly recommend you stay away from this approach—it’s just plain too confusing. It’s kind of a shame this technique isn’t easier to use, because it’s one way to work around the fact that you can’t call SetFocus from the Valid method of a textual control—in other words, from those controls where Valid really means a validity check.
Figuring out when the Valid for an object fires was confusing in FoxPro 2.x and it’s still somewhat confusing. For controls that accept text (the same ones that pay attention to Return .F.), it’s easy—it always fires when you leave, whether you click, tab, press Enter, or do something else.
Other controls fire their Valids only when they feel used. Clicking on a button or check box fires its Valid. So does pressing Spacebar or Enter. For a combo (with Style set to either combo or dropdown), Valid fires when you choose something from the list with the mouse or with Spacebar or Enter. A list’s Valid fires only when you double-click an item or press Enter.
Watch out for this one. The Valid and the LostFocus of a control don't fire if the user action doesn't cause the control to lose focus. We know that sounds obvious, but what might not be so obvious is the list of things that don't take focus from a control. For example, clicking a button on a toolbar doesn't take focus from the control that had it. Neither does making a menu selection. So why don't we have a bug icon on this one? Because it's not a bug. Some of the things that are on menus and toolbars shouldn't change the focus. Think of choosing, say, Edit-Cut from the menu or clicking a Cut button. Do you want the focus to leave the control you're on? Of course not. But if you're coding toolbar buttons to close forms, or menu options to bring up other forms, you may want to consider forcing the last item's Valid to fire to ensure that you're not leaving invalid data behind. See "It's a Feature, Not a Bug" for more on this issue. |
As an extra added attraction, several of the containers have When and Valid events of their own. The events for button groups fire right after the same event for the button itself. A grid’s When fires when you first enter the grid. The grid’s Valid fires when you leave the grid.
Finally, all the comments about which keystrokes fire Valids apply only when KEYCOMP is set to Windows. With KEYCOMP set to DOS, some different keystrokes fire objects’ Valids.
Last but not least (didn’t we just say “finally”?), if SET NOTIFY
is ON (not something we generally recommend), when a Valid returns .F., a wait window saying “Invalid Input” appears. If there’s code in the control’s ErrorMessage, that runs first, but then the “Invalid Input” window appears. To avoid this, either SET NOTIFY
OFF or RETURN 0 rather than .F. from the Valid.
Using WAIT WINDOW in the When of some controls interferes with the control's operation. For example, a WAIT WINDOW in a check box's When prevents the check box from changing value when you click it (though it does get focus). We think that the action needed to clear the WAIT WINDOW fouls things up. WAIT WINDOW NOWAIT is fine, though. |
* See if user input is acceptable in a text box for grade entry
PROCEDURE txtGrade.Valid
IF BETWEEN(This.Value,"A","F")
RETURN .T.
ELSE
WAIT WINDOW "Invalid grade. Try again" NOWAIT
RETURN .F.
ENDIF
Control Arrays, ErrorMessage, GotFocus, LostFocus, Set KeyComp, Set Notify