Jim Booth
Visual FoxPro provides for inheritance. Inheritance is the incorporation of data and behavior into one class from another class. In VFP, inheritance is by exceptionthat is, the behavior is inherited unless the subclass overrides it. Any code at all in an inherited method will override the superclass (parent class) behavior completely. Well, almost completely. In this column, Jim Booth investigates when and where code is overridden and what we can do to incorporate or eliminate behaviors from our subclasses.
Visual FoxPro comes to us with a set of base classes. All classes we design must originate from one of the VFP base classes. If we create a command button class, it must be a subclass of the VFP CommandButton base class. Once the new button class is designated to inherit from the CommandButton base class, the new button will have all of the properties, methods, events, and behavior that the CommandButton base class has. This is inheritance, and VFP supports it.
Take this
For the sake of our discussion, lets assume that we have a text box class (named MyText) inheriting from the VFP TextBox base class. If we place MyText in a form and run the form, we can type in the text box, and what we type is displayed in MyText. We see the characters typed because the default behavior, in the VFP TextBox base class KeyPress event, is to process the keystroke and display it in the text box. This behavior is the default behavior of the VFP base class. We can create a subclass of our text box class (named MyText2), and it will exhibit the same behavior because the behavior is being inherited from the TextBox base class to MyText and then to MyText2.
A step further
Lets return to the MyText class and write some code in the KeyPress. The following code segment is what well put in the KeyPress event of MyText:
* MyText KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
IF UPPER(CHR(nKeyCode)) = "A"
WAIT WINDOW "No A's Allowed"
ENDIF
Now create a form with MyText2 in it and run the form. When you type an A in MyText2, youll see the WAIT WINDOW message even though you didnt write the code in MyText2. Thats because the behavior is inherited from MyText. If you now edit the MyText2 class and open the KeyPress, youll see that the code isnt displayed. Thats because the code isnt inherited by the subclass; the behavior is inherited.
Now for some fun
VFP implements an inheritance model called program by exception. Program by exception means that a subclass will inherit behavior from its superclass (parent class) as long as no code has been written in the subclasss event or method being inherited. If one line of code (even a comment) is written in the event or method of the subclass, then the inheritance of the behavior of the superclass is severed.
To prove this, add the following code in the KeyPress event of MyText2 class:
* MyText2.KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
Now run the form with MyText2 again, type A into the text box, and youll see that the WAIT WINDOW doesnt get displayed. This is because the comment you added to MyText2.KeyPress has severed the inherited behavior from MyText.
But wait, didnt we see the A echoed in the text box? Yes, we did. Isnt the behavior of displaying the characters part of the text box base classs default behavior? Yes, it is. Then why did it get inherited if we severed the inheritance by adding code?
The answer is that the default behavior of a VFP base class is handled differently. Its always inherited, and it automatically executes after any code you add to the event or method.
One at a time
First, lets focus on the MyText behavior. How do you handle the situation where, in a subclass, you need to add behavior and also retain what would be inherited? VFP gives us two methods for forcing the superclasss behavior to execute: 1) the DoDefault() function; and 2) the scope resolution operator (::).
The DoDefault() function calls the same method in the superclass and executes the code there. Lets modify the code in MyText2.KeyPress to be as shown here:
* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
DoDefault()
Run the form again, and type in the text box. This time an error occurredwhy? Its simple, reallythe KeyPress event of MyText2 ran, and it received two arguments from VFP: nKeyCode and nShiftAltCtrl.
Then the comment severed the inheritance. Finally, the DoDefault() called the superclasss KeyPress event. However, the DoDefault() didnt pass the two arguments, so MyText.KeyPress did what VFP does when a parameter is omitted from the callit defaulted them both to .F.. When the line IF UPPER(CHR(nKeyCode)) was encountered, it found nKeyCode is a logical and CHR() wants a numeric; hence the error. First lesson: If you use DoDefault(), be sure to pass the parameters back to the superclass.
Fix the code in MyText2 by adding the two parametersDoDefault(nKeyCode, nShiftAltCtrl)and run the form again. The error is gone.
What about the scope resolution operatorhow does that work? Modify the code in MyText2.KeyPress as shown here:
* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
MyText::KeyPress(nKeyCode, nShiftAltCtrl)
Run the form again, and youll see the same behavior as with the DoDefault(). Which one should we use? This is a matter of opinion, but I say we should use DoDefault(). Why? Because DoDefault() always runs the code in the class immediately above the one with the DoDefault(), and this is how classes should behave. A subclass-to-superclass relationship can be called an "is a" relationshipthat is, MyText2 is a MyText, which, in turn, is a TextBox. The scope resolution operator allows you to call to any class in the hierarchy by explicitly naming the class on the left of the operator. While this might seem to be more flexible, it allows for very poor class design.
If you have a subclass and, for whatever reason, you want to call inherited behavior bypassing the immediate superclass, then your MyText2 is probably not a MyText. In other words, MyText2 should probably be a subclass of MyTexts superclass and not of MyText itself. If DoDefault() doesnt get you what you want, then you should review the class design and not succumb to using the scope resolution operator to get around a poor design.
Next
Okay, so we can make the superclass behavior execute even if we add behavior in a subclass, but what if we dont want the base classs behavior to execute? VFP gives us the NODEFAULT command for this purpose. NODEFAULT stops the execution of inherited behavior including the VFP base class default behavior.
Change the code in MyText2.KeyPress to this:
* MyText2 KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
* Testing the program by exception
NODEFAULT
Run the form and type in the text box. Youll see nothing show up in the text box because youve stopped the VFP base class behavior of processing the keystroke.
Now for something completely different
A silly demonstration, true, but its a clear one. Lets go a step further. Edit the MyText class, and in the properties sheet, right-click the KeyPress event and choose Reset to default. Now run the form, and type in the text box (watch carefully exactly what happens and when). Notice that when you type A in the text box, you first see the WAIT WINDOW message, and then you see the A in the text box. How can you make it work the other way aroundif you want to see the A first and then see the WAIT WINDOW? Edit MyText and include the following code in the KeyPress event:
* MyText KeyPress
LPARAMETERS nKeyCode, nShiftAltCtrl
DoDefault(nKeyCode, nShiftAltCtrl)
IF UPPER(CHR(nKeyCode)) = "A"
WAIT WINDOW "No A's Allowed"
ENDIF
NODEFAULT
Run the form and type in the text box. This time, youll see the A in the text box first, and then the WAIT WINDOW appears. DoDefault() doesnt only execute the superclasss behavior, it also executes the base class behavior.
Remove the NODEFAULT at the end of this code and run the form again. Type A once in the text box. What did you see? Each time you dispatched the WAIT WINDOW, the letter A was again displayed in the text boxthe display was doubled. Why? Because the DoDefault() executed the base class default behavior of processing the keystroke and displayed the A, then the WAIT WINDOW appeared. When you dispatched the WAIT WINDOW, the base class behavior executed again. VFPs base class behavior always executes after your code unless you do something to stop it (NODEFAULT).
So what does this all mean?
VFP implements programming by exception for behavior inherited from your classes. The default behavior of the VFP base classes arent programming by exception; rather, you need to actively stop the base class behavior using NODEFAULT.
DoDefault() and the scope resolution operator can be used to force the execution of the behavior in a superclass that would otherwise have been severed. DoDefault() also causes the VFP base class behavior to execute. The VFP base class default behavior automatically executes after your class code finishes. So if you use DoDefault() during your code, youll likely need NODEFAULT at the end of the code to prevent the base class behavior from executing twice.
Jim Booth is a Visual FoxPro developer and trainer. He has spoken at FoxPro conferences in North America and Europe. Jim has been a recipient of the Microsoft Most Valuable Professional Award every year since it was first presented in 1993. Hes co-author of Effective Techniques for Application Development and Visual FoxPro 3 Unleashed and is contributing author for Special Edition Using Visual FoxPro 6.0. Jim is also the technical editor for Database Design for Mere Mortals. Visit his Web site at
www.jamesbooth.com. 203-758-6942, jbooth@jamesbooth.com.