Visual FoxPro developers have been using an event-based methodology for a very long time.
For most purposes, events are what drive the development effort. The user clicks a button, causing an event to fire, and the developer writes code to react accordingly. All of this happens very transparently and without difficulty for either party. However, from a developer's point of view, there also isn't much flexibility in this approach. But in VFP 8, event handling is changing for the better.
First of all, don't worry! Things are still just as easy and painless as they were in previous versions. But Visual FoxPro 8 adds a whole lot of flexibility to the event mechanism. Before we delve into the details of the new functionality, let's examine what we had in the previous version.
For Visual FoxPro developers, an event and the code that reacts to it are one and the same thing. But that's actually incorrect. An event is a very different animal from the code that runs when an event occurs. Let's look at a simple example.
Let's assume we drop a button on a form. The button is a Visual FoxPro standard control (base class) that comes with a whole lot of functionality that is exposed to the developer as properties, methods, and events. This is known as the button's "interface." By that we mean programming interface, and not the user interface.
One of the most frequently used features of a button is the Click event. When we double-click this event in design mode in the property window, Visual FoxPro opens the code window, showing the Click() method. For this reason, most developers think that the Click() method and the Click event are one and the same thing. That's not the case.
What is interesting here is that we can have more than one handler for an event. In fact, we can add as many different event handlers to each event as we want!
When we add code to the Click() method, we are not really touching the button's event at all. Instead, we simply put code into a method that goes with the button's instance. When we compile the whole thing, Visual FoxPro realizes that the code we wrote is meant to go with the button object. Visual FoxPro can also look at the button's interface to see that the button might occasionally raise a Click event. Therefore, VFP compiles our code and links it to the button by simply using a naming convention that says the whenever there is a method with the same name as an event, that method is executed when the event occurs.
Note that this fact is specific to Visual FoxPro. If the designers of VFP had decided that the name of a click method would be prefixed by "On," then the method that goes with the Click event would have to be called "OnClick". (This, in fact, is a convention used by other development environments). So the name itself isn't really important. What's important is that there is some naming convention that allows the compiler to automatically link our code to the event. Otherwise, we'd have to establish the link manually, which would be a lot of work.
But sometimes, this may be desired! Perhaps we would like to dynamically attach and detach event handling code. Or perhaps we would like to handle different events with the same event handler. And that's where VFP8's new event binding comes in!
Manual Event Binding
So let's assume we have the following method as a member of our form, and would like to bind it to a button on our form:
FUNCTION ShowMessage() MessageBox("Button Clicked") ENDFUNC
We could do so by using the new BindEvent() function. We could use that function whenever and wherever we want. In this example, the form's Init() method might be the best place:
FUNCTION Init() BINDEVENT(THIS.command1,"Click",; THIS,"ShowMessage") ENDFUNC
The first two parameters define the source of the event (object, plus name of the event), the second pair defines the handling object and the method we want to delegate the event to.
If you run this code inside a form and click the button, you will see the message box firing. Note that this doesn't influence the original click event at all. You can check this by simply adding another message box to the button's Click() method. In that case, the ShowMessage() method will fire first (whenever a Click event is raised), and then the Click() method will follow.
If you would like the sequence to occur the other way around, with Click() firing first, you can indicate so by passing an optional 5th parameter to the BindEvent() function:
Aside from the sequencing, what is interesting here is that we can have more than one handler for an event. In this case, we have our ShowMessage() method as well as the default Click() method. And it doesn't stop there! In fact, we can add as many different event handlers to each event as we want!
Just like we bound event handlers to events, we can also unbind events. So let's assume we want our ShowMethod() to only handle the very first Click event the button raises, but not subsequent ones. In that case, we could add the following code to the ShowMessage() method:
FUNCTION ShowMessage() MessageBox("Button Clicked") UnbindEvents(THIS.command1,"Click",; THIS,"ShowMessage") ENDFUNC
Of course, this assumes that the ShowMessage() method is designed to be attached to THIS.command1, and no other object. In real-life scenarios, you may not want to do that, because these types of dynamic event handlers generally are very generic so they can be attached to different objects. However, it would be easy enough to put this code in other methods or event handlers as well. We will also explore some generic ways to discover current bindings a later in this article.
Note that there also is a simpler version of UnbindEvents() that receives one parameter (an object reference):
This will automatically unbind all event handlers on the THIS-object (which would include our ShowMessage() method). In addition (and this is important to know), it will also unbind all event handlers in other objects, that are binding to events that may occur on the THIS object. Therefore, if another object binds to the Activate event of the form (for instance), that binding will be released as well. While this is useful in some scenarios (perhaps we want to release the THIS-object from memory), it is also a rather barbaric approach, compared to the surgical accuracy of the 4-parameter version.
Binding to Multiple Events
It is often desirable to bind one event handler to multiple events. This is easiest explained through an example: In many Visual FoxPro forms, developers us a Validate() method to verify data entered in the form. This method then may be triggered before the data gets saved, or when a certain button is clicked, or perhaps when the data is actually changed. Either way, the Validate() method is called manually. With dynamic event binding however, that method could be called whenever any of the data changes by binding to the Valid events on the controls of the forms. The following code illustrates how to accomplish that task:
FUNCTION Init() LOCAL loControl FOR EACH loControl IN THIS.Controls IF PEMSTATUS(loControl,"Valid",5) BINDEVENT(loControl,"Valid",; THIS,"Validate") ENDIF ENDFOR ENDFUNC
This simply iterates over all the controls on the form the Init() method belongs to, checks if the control has a Valid event, and if so, binds the custom Validate() event handler method to that event. This results in the Validate() method handling all Valid events that may occur on any control. Note that this doesn't influence any other event handler (such as the standard Valid() method on each control).
Note that some controls behave slightly differently than others when we manually bind to events such as When() and Valid(). Textboxes, for instance, require that there is code in the actual Valid() method before the manually bound code will fire. This is an inconvenience, but it has to do with some of FoxPro's internal architecture. You can find more information about these special cases in the Visual FoxPro documentation.
The ability to bind a single event handler to multiple event sources is very convenient in a number of scenarios. For instance, this technique can be used to add debug or logging code to a project. The code in Listing 1 shows a form class that has the ability to log every button click that happens on the form. (I am showing this in full source code in this example, but of course this works very similarly in visual classes.)
The code should be pretty self-explanatory, perhaps with the exception of the call to AEVENTS(). Used as shown in this example, AEVENTS() will return a reference to the object that fired the event. This is important in many scenarios where an event handler can dynamically be bound to different objects, yet a reference to the object is required (such as querying the object name in our example).
Note that some controls behave slightly differently than others when we manually bind to events such as When() and Valid().
Hooks are a popular mechanism to create extensible software. Microsoft uses this mechanism quite a lot in Visual FoxPro itself. A good example for this is the Class Browser. It exposes an "interface" that allows developers to write AddIns by using the browser's proprietary mechanism to hook its events and methods to those written by the developer. The problem with this approach (besides the proprietary nature of the mechanism) is that Microsoft has to write all those "hooks" manually. This is a lot of work, which is the main reason this type of mechanism isn't used in all the software written in VFP. In the typical everyday project, developers simply don't have the time to add this functionality. Therefore, most applications do not offer "hooks."
With dynamic event binding, however, we get this type of functionality automatically. Every application that fires events can be extended using manual event binding. This is even true for internal VFP objects, such as the VFP main screen. In older versions of Visual FoxPro, it was often not possible to use the default screen (VFP's main window) as the application window, since one had very little control over this object. For instance, you could not react programmatically when the screen was resized.
Now, however, we can "hook" this event and react to it the same way we otherwise would implement in subclasses.
Events vs. Methods
One of the little known facts is that Visual FoxPro internally handles different events in different ways. This has to do with Visual FoxPro's history. Some events, such as When() and Valid(), go way back to pre-Visual versions, while others were introduced with Visual FoxPro 3.0. The difference is that some events are true events, while others are just simple methods that are being called when certain things happen, which makes them look like true events for most purposes.
For manual event binding, however, the difference is significant. It would be very difficult for most developers to first figure out whether an event is a true event or just an automatically fired method. For this reason, the Visual FoxPro team built the BindEvent() function so that it also allows you to bind any type of handler method to any other method. This way, we can bind to all sources of events equally.
This has the convenient side-effect that we can bind handler methods to all types of methods, no matter whether they are internal methods or custom methods defined by the developer. For instance, we could add a method called "SomeMethod" to the form. We can then add another method called "SomeOtherMethod" to the same form and bind that method to the first method:
FUNCTION Init BindEvent(THIS,"SomeMethod",; THIS,"SomeOtherMethod") ENDFUNC
Now, whenever SomeMethod() gets called, SomeOtherMethod() fires, as well.
Sometimes, this behavior may not be desired. For instance, one might want to bind to the Click() event, but not to the Click() method. This can be done using an optional flag:
Every application that fires events can be extended using manual event binding.
The "2" in the fifth parameter indicates that we do not want to bind to the Click() method, but only to the event. If someone clicks on the button, the handler method will fire. However, the following code would not invoke the handler method:
Of course, this only works because Click() is a true event (unlike Valid(), for instance).
BindEvent() is a very powerful new feature. It is probably not a feature that every developer has been waiting for, but those of you who have a need for this feature need it badly.
If you have any questions about this feature, feel free to send me an email.