Listing 8 shows a flexible variant of listing 7 where the pre-processing hook's return value controls the standard method execution. This is a crude NODEFAULT-like implementation, controlled by the pre-hook’s return value.
Listing 8. A pre-process hook method (in a separate object) controls the execution of the standard method.
Listing 9 dramatically improves on Listing 5 because it uses both pre and post hooks, allowing you to completely bracket transformations. Listing 9 uses the following heristic:
Events are pre-hooked,
events then call a method, and
that method is post-hooked.
In this separated "pre" and "post" architecture it is much safer to subclass the event or the method without scope-resolution (::) entanglement.
FUNCTION Click
Listing 9. A dramatically more flexible version of Listing 5. The pre-hook is called before the event code, the event code calls a method, and a post hook ends the method. Thus we have both pre- and post-hooks and few of the subclassing problems we'd have if both hooks were in the event code.
I personally think that code structured like that of listing 9 is very slick. Note that the event (like click() and dblclick()) fires the "pre" hook predictably near the beginning of its execution, and the method (the thing invoked by event) fires the "post" hook predictably the end of its execution. Thus we have, in effect, a hook on the way in, and another on the way out. Other advantages:
However I have just one problem with all this: the hooks in Listing 9 are dependent on the hook classes supporting a particular interface. One promising alternative is to vastly simplify the hook interface by having a single hook method to which we pass an appropriate context identifier, like PROGRAM(). This leads to our last implementation topic, which is to use procedural hooks.
Listing 10 shows a variant of Listing 9 that decouples the hook from its implementation.
Listing 10, a component with a hook before calling a Click implementation method. The Click implementation method contains its own hook at its conclusion. Note that the hook calls are made to a hook manager method which permits a clean and generic interface.
The key here is the clean and invariant call, THIS.oApp.HookMgr( PROGRAM()), which allows us to have a single hook method serve for all application hooks. Of course, considerable logic may be required within the oApp::HookMgr() method but, on the other hand, this would allow us to table-base the hook operations, which is infinitely more flexible than hard-coding hooks. This mechanism is similar to the one used internally by the VFP Class Browser. More on this in the next article.
VFP Class Browser: The VFP Class Browser is a real hookfest. By using its AddIn() method, and by specifying a method to hook, you can permanently register custom behavior for a variety of events. Registering the add-in is persistent because the AddIn() method creates a record in BROWSER.DBF, which lives in your HOME() directory.
Markus Egger's PowerBrowser: The public-domain PowerBrowser tool is an example of how a fully hooked application like VFP's Class Browser can be considerably adapted.
The INTL Toolkit: The INTL Toolkit, which I wrote, uses hook objects throughout.
The Visual FoxPro class browser can register and invoke addins. These addins are external programs you can create to customize, control, and extend the browser. This makes the browser very flexible and slick! Here we'll examine the browser's addin interface, and then we’ll see a way you can use addins to bring almost limitless customizability and extensibility to your apps.
By the way, there isn’t much original thinking on in this section. Ken Levy originally created and coded these addin concepts. I’m just the one who was curious enough to ask him about it, and happy to share what I learned.
To introduce implementing our own addins, we’ll first look at how class browser addins work. The best way to learn all about addins is to create one. Once we understand how addins work, we’ll extract the browser’s addin functionality into a reusable component.
Here is a handy class browser addin that adorns browser with a "Class Backup" button which, when clicked, creates a backup of the current class library. Look at the listing: the addin program receives an object reference parameter, and henceforth the addin has its way with it. As you can see in Listing 11, Browser addins can be wonderfully straightforward to write.
Listing 11. A simple class browser addin: A useful backup button to protect you from botched changes.
You register addins with the browser's Addin() method, which adds, modifies, or deletes a record about the addin in BROWSER.DBF. Here's the addin() method’s prototype:
The browser programming interface is documented in VFP help under "class browser". Addin()’s important parameters are:
(It’s helpful to remember that all these configuration items are stored in individual memo fields in BROWSER.DBF.)
So we could say _oBrowser.AddIn("VCX backup", "ClassBackUp.PRG") to register our addin, the choice „VCX Backup“ would appear in the browser’s Addins menu, and our backup button would only materialize we’d made this selection.
Or we could wire ClassBackUp.PRG to the browser’s INIT() method like this: _oBrowser.AddIn("VCX backup", "ClassBackUp.PRG", "Init"). Lo and behold, our magic VCX backup button is there every time we use Browser because ClassBackUp.PRG now runs perpetually in browser's init().
But how does the class browser do this? Because most of browser's events contain hook operations that can invoke pre-registered addins. We’ve discussed hook operations before, so let's quickly summarize what we know about hook operations:
Engineering addins requires the following things:
Addins can be implemented in a variety of ways depending on how the hook operation is structured. Here are some good tactical principles to guide the design of hook operations.
Keep code short because, among all the other things, brief code increases the likelihood that predictable junctures are proximate to critical junctures.
Events call methods to avoid placing implementation code in the interface.
Events are pre-hooked to allow pre-processing and control over system’s reaction to the event
Methods are post-hooked to allow modifying the products method.
These four points are all in illustrated in the following code for a „Save“ button“
Listing 12. A hookfest.
Note that in the code above, the hook operation code is the same in Click() event as it is in the Save() method. This is a nice bonus.
Here is a simple example of a hook system. Here we have a form with a textbox and a combobox connected by hooks as shown in figure 6.
Some things of note in the sample code:
Hooks are chained. The combobox, for example, chains through a number of objects, first picking up Dropdown() and Interactivechange() behavior, followed by the Form's hooks, which in this case attach a 2-part Rightclick() behavior.
The Textbox's RightClick() is supplemented by the Form's RightClick() and that of the Form's hook, in this case some additional debug choices (full functionality not wired-in).
Note the GarbageCollect() procedures which are woven into the Release() processes to clean up these pointers. Without this, the form will never release without CLEAR ALL. In some versions of VFP, expect a GPF without this pointer clean-up.
Figure 6: The sample provided creates this system. The combobox hook chain starts is composed of two behavior objects before hooking to the form and its hook. The textbox hook chain is similarly extended by the form and its hook object.