Session E-EVNT

Event Driven Programming
in Visual FoxPro

Ted Roche
Blackstone Incorporated


Overview

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.

Event

Applies to:

What it does / what to put there

INIT

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.

ERROR

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.

DESTROY

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

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

DRAGOVER, DRAGDROP

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.

MOUSEMOVE

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.

CLICK, MOUSEDOWN, MOUSEUP

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

Mouse click

UIENABLE

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.

RIGHTCLICK

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

Right mouse click on control.

GOTFOCUS, LOSTFOCUS

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

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

VALID, WHEN

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.

ERRORMESSAGE

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"

MESSAGE

same as above

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

KEYPRESS

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

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

MOVED

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

Fires when the object has been moved.

RESIZE

same

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.

ACTIVATE, DEACTIVATE

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

RANGEHIGH, RANGELOW

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.

DOWNCLICK

ComboBox, ListBox, Spinner

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

LOAD, UNLOAD

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.

PAINT

Form, Toolbar

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

BEFOREOPENTABLES, AFTERCLOSETABLES

Data Environment

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

AFTERDOCK, BEFOREDOCK, UNDOCK,

Toolbar

Code which can run while user is manipulating a toolbar.

BeforeRowColChange, AfterRowColChange

Grid

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

DELETED

Grid

When user marks or unmarks a row for deletion.

SCROLLED

Grid

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

DROPDOWN

ComboBox

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

TIMER

Timer

Fires when Timer is enabled and Interval has passed.

QUERYUNLOAD

Form

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

READACTIVATE, READDEACTIVATE, READSHOW, READVALID, READWHEN

Form

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.