Session E-EVNT

Event Driven Programming
in Visual FoxPro

Ted Roche
Blackstone Incorporated


Visual FoxPro's new event model, combined with the robustness of the object model and the addition of many new events, allows truly modeless programming with far finer control of the behavior of your system. The Foundation Read has been replaced with two simple commands to hook your application directly into FoxPro's native event loop. New events are available to programmers with exciting implications for application development.

"An action, recognized by an object, for which you can write code to respond. Events can be generated by a user action, such as clicking the mouse or pressing a key, by program code, or by the system, as with timers."

- FoxPro Help File

The event firing sequence can be some of the most intricate functionality to understand. Typically, a container cannot perform its action until its contents exist, therefore, objects are created from the inside out: textbox–›column–›grid–›page–›pageframe, and destroyed in the opposite fashion, from the outside in, imploding.

The Data Environment wraps around the form's (or formset's) event model, instantiating itself before the form and destroying itself after the form is released.

An event is usually detected at the innermost control with "jurisdiction" over the occurrence. A command button will detect a click, rather than the containing Container, or the form. If the control is not able to detect that type of event, the event may go unnoticed, or the containing control may detect the event.

The Foundation read is no more, except for legacy code we attempt to migrate to Visual FoxPro. Forms are it. The concept behind Visual FoxPro is that the initial application environment can be set up, a menu can be hoisted to the top of the screen, and READ EVENTS will hold the entire application together until the user chooses to quit. CLEAR EVENTS terminates your application's attachment into FoxPro's native event loop.

Code is added to the events of individual controls to provide a responsive user interface at the lowest level possible. Depending upon the needs of the client, this interface can be made far more intuitive than the standard "tab from control to control" philosophy to include sliders, drag-and-drop events, and applications which sense and respond immediately to mouse movements and keystroke events.


Applies to:

What it does / what to put there


All but Column, Header, Page, Separator

Fires when the object is created, optionally accepts parameters. If it returns .F., object is not created. Contained objects fire before containers, in the order added.


All but Column, Header, Page, Separator

Fires when an error occurs in the method of an object - passes error #, method name and line number. If code is present in the object's Error event, this code runs instead of the default ON ERROR handler.


All but Column, CommandGroup, Header, OptionGroup, Page, and Separator

Code runs just before an object is released. Containers fire before contents.


All but Column CommandGroup, Cursor, Custom, DataEnvironment, FormSet, Header, OptionGroup, Page, Relation, Separator and Timer

Fires during and upon completion respectively of a drag & drop operation. Code must include parameters statement to accept the dragged object reference and mouse coordinates.


All but Column, CommandGroup, Cursor, Custom, DataEnvironment, FormSet, Header, OptionGroup, OLEControl, OLEBoundControl Page, Relation, Separator and Timer

Tracks mouse movements over an object. Also passes status of Ctrl-Alt-Shift keys, as well as left, middle and right mouse button statuses.


All but, CommandGroup, Cursor, Custom, DataEnvironment, FormSet, Header, OptionGroup, OLEControl, OLEBoundControl Page, Relation, Separator and Timer

Mouse click


CheckBox, ComboBox, CommandButton, CommandGroup, Container, Control, EditBox, Grid, Image, Label, Line, ListBox, OLEBoundControl, OLEControl, OptionGroup, PageFrame, Shape, Spinner, TextBox

Fires when control becomes visible because of activation of container, such as PageFrame.


Above, plus Form, Header, OptionButton, OptionGroup, but NOT OLEBoundControl, OLEControl

Right mouse click on control.


CheckBox, ComboBox, CommandButton, Container, Control, EditBox, Form, ListBox, OLEBoundControl, OLEControl, OptionButton, Spinner, TextBox

Occurs when the control is tabbed to, or clicked on.


CheckBox, ComboBox, CommandButton, CommandGroup, EditBox, Grid, ListBox, OptionButton, OptionGroup, Spinner, TextBox

Good old WHEN and VALID, fire before accepting a change (after receiving focus) and after a change is made.


CheckBox, ComboBox, CommandButton, CommandGroup, EditBox, ListBox, OptionButton, OptionGroup, Spinner, TextBox

When VALID returns a .F., allows display of an error message. "Included for backward compatibility"


same as above

Displays status bar text. Another " backward compatibility." Property StatusBarText provides similar capabilities.


CheckBox, ComboBox, CommandButton, EditBox, Form, ListBox, OptionButton, Spinner, TextBox

Allows processing of input keystroke-by-keystroke, rather than waiting for input to be completed.


Column, Container, Control, Form, Grid, OLEBoundControl, OLEControl, PageFrame, Toolbar

Fires when the object has been moved.



Fires when the object has been resized.

InteractiveChange, ProgrammaticChange

CheckBox, ComboBox, , CommandGroup, EditBox, ListBox, OptionGroup, Spinner, TextBox

What UPDATED() always should have been, but at a finer level. Fires each time a change is made via mouse or keyboard, even before focus has shifted from the control. INTERACTIVE detects user changes, PROGRAMMATIC changes performed in code.


Form, FormSet, Page, Toolbar

Similar to the 2.x Screen's show clause. Occurs when container gets the focus or Show() method runs. Toolbar.Hide() also runs DEACTIVATE


ComboBox, Listbox, Spinner, TextBox

Dual functions. For ComboBox and ListBox, returns the initially selected element when the control gets the focus. For Spinners & TextBoxes acts as a RANGE test, returning a numeric when focus to the control is lost.


ComboBox, ListBox, Spinner

Not to be confused with MOUSEDOWN, fires when the down- or up-ward-pointing arrow is pressed.


Form, FormSet

Load is the first form-based event (after Data Environment events) and is a great place to make last minute changes to the global characteristics of a form. UnLoad is the last event to fire.


Form, Toolbar

When the item re-paints. CAUTION: don't RESIZE or refresh() objects within PAINT or a "cascading" series may occur!


Data Environment

Wrappers around the automatic behavior of the Data Environment. Occurs before OpenTables() method and after CloseTables() methods.



Code which can run while user is manipulating a toolbar.

BeforeRowColChange, AfterRowColChange


Before the Valid of the row or column of the cell being left, and after the When of the cell being moved to.



When user marks or unmarks a row for deletion.



User movement, parameter will return whether by cursor keys or scroll bars and which one.



Fires after DOWNCLICK, to allow interactive changes to the contents of the drop down list.



Fires when Timer is enabled and Interval has passed.



Allows testing the ReleaseType property to determine if a form is being released using the close box or programmatically.



Similar to 2.x READ model, only works in 'Compatibility' modes




Experiment. 90% of the time the standard WHEN and VALID will provide all the functionality needed in data entry fields. Specialized input fields, such as Spinners, have finer control. Click is a more intuitive place to put button firing code than VALID, but either (though not necessarily both!) work. Add new Events to your arsenal as the need arises. Anticipate some great third party tools which know how to really take advantage of all the new features.

The included application demonstrates several of the nicer features, including resizing, sensing mouse movements and detecting mouse and keyboard presses.

Our table above shows how events should work. Occasionally, you will run into a non-sensical behavior. In this case, the Events tool included on the source code disk can make clear what is actually happening. For example:

BeforeOpenTables actually fires during the OpenTables event, after any custom code in the OpenTables event, but just before the default behavior of the tables opening. We find the naming of this event pretty confusing.

The InteractiveChange event fires during most changes to a control, but not all. InteractiveChange is hooked up to the Value property. If a change does not affect the Value, it may not be trapped. For example, a multi-select ListBox will not change its focus (or associated Value) during a Control-Click to de-select a previously selected item, and hence the InteractiveChange event will not fire. Using the Click event in this case will trap the event.