The Visual FoxPro Object Model

Excerpted from The Visual FoxPro 3 Codebook, by Y. Alan Griver

July 7, 1995

Introduction

The Microsoft ®Visual FoxPro® object model is based on a strong foundation of object-oriented principles. User-defined classes, protected class members, inheritance—everything you would expect to find in a robust object model. The model includes a set of built-in classes that you use as a basis for creating your own classes. These built-in classes are called base classes.

Base classes are grouped into controls and containers. The difference between the two is that a container can include other controls or even other containers, while a control cannot. (The "control" control is somewhat of an exception to this rule, since it allows us to refer to multiple controls as if they were one control.) A good example of a container is a form that contains a group of text boxes. The form is referred to as the parent, while the text boxes are referred to as children (or child controls).

Here is the current list of base classes:

Non-Visual
Controls
Non-Visual
Containers
CheckBox Container
ComboBox X FormSet
CommandButton Form
Control Grid
X Custom Column
EditBox X PageFrame
Header Page
Image ToolBar
Label OptionButtonGroup
Line CommandButtonGroup
ListBox
Shape
Spinner
TextBox
X Timer

Base Class Properties

An object has properties, which are used to describe the object or hold values that represent the object's state. All Visual FoxPro base classes share a common minimum set of properties:

Property Description
Class The name of the object's class
BaseClass The name of the object's base class
ClassLibrary The full path of the class library where this class is defined
ParentClass The name of the class of the object's parent class

You are free to add custom properties to any new class you create. I'll show you how in just a moment.

Base Class Events and Event Methods

Most objects also exhibit some kind of behavior in the form of methods. In Visual FoxPro, an object can also respond to events that occur in the system. For example, when the user clicks a command button, Visual FoxPro invokes the Click event. You can place code in a command button's Click() event method that will automatically run whenever the button is clicked.

All Visual FoxPro base classes can respond to a common minimum set of events:

Event Description
Init Invoked when the object is created
Destroy Invoked when the object is released
Error Invoked when an error occurs inside one of the object's methods

Class Definition Syntax

While it's possible (and probably more desirable) to create all your classes, including non-visual ones with the Class Designer, it's easier and more straightforward in a chapter of this nature to demonstrate the features of the language in code. The code that follows can be typed directly into a PRG. Just be sure to place the CREATEOBJECT() function calls before any class definitions!

When creating new classes, we use the new DEFINE CLASS command:

DEFINE CLASS MaintenanceForm AS Form
ENDDEFINE

DEFINE CLASS Transaction AS Custom
ENDDEFINE

These statements create new classes based on Visual FoxPro's built-in classes. More precisely, we are subclassing a Visual FoxPro base class. Through the power of inheritance, we automatically inherit any properties and methods defined in the class that we are subclassing, and thus can treat them as if they were actual members of our new classes:

*-- Create an instance of the Transaction class and print the
*-- value of the Class property.
oCreditCardTransaction = CREATEOBJECT("Transaction")
? oCreditCardTransaction.Class     && Prints "Transaction" on screen

Let's modify our transaction class definition to show how the Init() and Destroy() event methods work:

DEFINE CLASS Transaction AS Custom
  FUNCTION Init()
    WAIT WINDOW "Creating object"
  ENDFUNC

  FUNCTION Destroy()
    WAIT WINDOW "Destroying object"
  ENDFUNC
ENDDEFINE

The Init event is invoked by Visual FoxPro whenever an object is created. The Init event causes code defined in the Init() event method to be executed. The generic object-oriented term for this type of event method is constructor. It is commonly used to initialize properties of the object, or to ensure that the environment is correctly set up before the object is used.

The Destroy event is invoked by Visual FoxPro whenever an object is destroyed. The Destroy event causes code defined in the Destroy() event method to be executed. The generic object-oriented term for this type of event method is destructor. It is commonly used to clean up the environment when an object is being released.

Note   If you looked carefully, you might have noticed the empty parentheses after the method name. This is not an error. In fact, instead of using the PARAMETERS statement (or, more accurately, LPARAMETERS) you can now define parameters within parentheses immediately after the method name. The empty parentheses are a matter of coding style and are strictly optional.

Creating Instances

As you may have already guessed, the syntax for creating an instance of a class is:

oObjectReference = CREATEOBJECT(cClassName)

The CREATEOBJECT() function accepts a class name as a parameter and returns a reference to an object. This object reference is really just a memory variable with a data type of "O", which stands for object. Variables that represent objects function are very similar to other types of variables. For example, it is perfectly legal, although not good programming practice, to assign a value of a different data type to a variable that represents an object:

oTransaction = CREATEOBJECT("Transaction")
oTransaction = "A line of text"    && an object is just a variable

Note that when passing objects as parameters to a function or method, the object is always passed by reference, never by value. Also, when assigning an object to another memory variable, the new variable is a reference to the same object.

*-- Both oTransaction2 and oTransaction refer to the same
*-- object in memory
oTransaction2 = oTransaction

Releasing Instances

You can release an object the same way you release a memory variable:

RELEASE oTransaction

When you release an object, its Destroy event is fired, and any code defined in the Destroy() event method is executed. It's important to note that the object remains in memory until all references to it have been released.

Subclassing User-Defined Classes

Not only does Visual FoxPro allow you to create new classes based on the built-in base classes, but you can also create new classes based on your own user-defined classes. For example:

DEFINE CLASS BaseForm AS Form
ENDDEFINE

DEFINE CLASS MaintenanceForm AS BaseForm
ENDDEFINE

Here we first define a new form class called BaseForm, and then use that form class as the basis for yet another new form class, MaintenanceForm. If we wish, we could create a new class based on the MaintenanceForm class, and then create a new class on that, and so on. I would recommend keeping the class hierarchy depth as "shallow" as possible, without placing a specific number on just how deep to go. Use your best judgment.

We Pause for a Look at Our Dictionary . . .

Before we get too deep into a sea of terminology, let's pause for a moment to examine some terms.

In past articles, we defined the term "superclass," which refers to any class that is being used as the basis for creating other classes. You may have seen other object-oriented texts refer to a superclass as a "base class." In fact, this is acceptable terminology. However, it is important to understand that Visual FoxPro uses the term "base class" to refer to its own built-in class hierarchy, not to an arbitrary superclass that you create.

Visual FoxPro uses the term "parent class" to mean exactly the same thing as "superclass." This is unfortunate because it seems that most other object-oriented products and literature have standardized on the term "superclass."  Additionally, Visual FoxPro uses the term "parent" to refer to an object that contains other objects. It can get confusing when trying to talk about the class of an object's parent vs. an object's parent class—they are not the same thing.

I bring up this issue of terminology to help you avoid confusion when discussing object-oriented topics with your associates, or when reading a non–language-specific, object-oriented book or article. Just remember that in generic, object-oriented terms, "base class," "superclass," and "parent class" all mean the same thing. But remember that a "base class" is just a "parent class" that has special meaning in Visual FoxPro, and that "superclass" and "parent class" mean exactly the same thing, but "parent class" is preferred.

Back to Your Regularly Scheduled Object Model . . .

So how do we add custom properties to our classes? Here's the syntax:

DEFINE CLASS Customer AS Custom
  *-- Custom property definitions
  cName = "Ivar Jacobsen"
  nAge = 40
  lHasMethodology = .T.

  *-- Method definitions follow
ENDDEFINE

Basically, custom properties are defined before any method code for that class. You could also use this space to initialize built-in properties:

DEFINE CLASS MyForm AS Form
  Caption = "My Form"
  AutoCenter = .T.
  BorderStyle = 2
ENDDEFINE

Note that if you need to initialize a property to the result of an expression or UDF, you'll have to do this in the Init() event method for the class:

DEFINE CLASS Table AS Custom
  cFullName = ""

  FUNCTION Init()
    this.cFullName = DBF()
  ENDFUNC
ENDDEFINE

What's THIS All About?

In the Init() event method of the above example, I used the new this keyword to refer to the property of the class. Visual FoxPro has added this keyword, along with the thisform and thisformset keywords, to provide access to properties or methods that are scoped to the class, form, or formset, respectively. The following table illustrates this concept further:

Keyword Meaning
this Used in method code to refer to a property or method of the current class.
thisform Used in method code in a form to refer to a property or method of the current form. Can be used from anywhere within that form, including methods of controls on that form.
thisformset Used in method code in a formset to refer to a property or method of the current formset. Can be used anywhere within that formset, including methods of forms contained within that formset, or controls contained on any form in the formset.

What About Protection?

Encapsulation is the ability to bind both data and functions (or procedures) to a class. What if you have a situation where you have defined properties or methods for a class that you do not want to be accessed directly using the object.Property or object.Method() syntax? Like any robust object model, Visual FoxPro allows you to do this through the PROTECTED keyword.

DEFINE CLASS Customer AS Custom
  PROTECTED cName

  PROTECTED FUNCTION ChangeName(tcNewName)
    this.cName = tcNewName
  ENDFUNC
ENDDEFINE

If we instantiate an instance of class Customer:

oCustomer = CREATEOBJECT("Customer")

and attempt to access either the protected property or method of that class, we will get an error:

oCustomer.cName = "Grady Booc"          && Error!
oCustomer.ChangeName"Grady Booc")       && Error!

Why would you want to protect class members? Let's assume that certain properties of a class represented an object's state. If you couldn't prevent those properties from being accessed directly from outside the class, how could you ever guarantee the state of that object? If you protect the property, you could then create a custom method that would be used to assign values to that property. The method could contain validation code to ensure that the value is set properly. Since the property value is assigned in just one place, the code becomes much easier to debug and maintain.

Another reason for having the ability to protect members of a class is when you have utility methods that serve a particular purpose for a specific class, but are not meant to be called from outside that class. Leaving those methods unprotected could have disastrous results!

Add Those Objects!

A property of a class is not limited to being just a simple variable; it can also be an object of another class. For example, if you want to add a command button to a form in code, you would use the following syntax:

DEFINE CLASS MyForm AS Form
  ADD OBJECT oCommandButton AS CommandButton
ENDDEFINE

Once you create an instance of this form, you could then refer to the oCommandButton just as you would refer to a normal property:

oMyForm = CREATEOBJECT("MyForm")
? oMyForm.oCommandButton.Caption      && Prints the button's caption 

The form is now considered the parent of the command button. In fact, the form can be accessed by referencing the command button's Parent property. From the Click() event method of the command button:

WAIT WINDOW this.Parent.Caption       && Print the form's caption 

Conclusion

The Visual FoxPro object model is based on a solid foundation of object-oriented concepts that have been in place for years. As a result, developers will be able to create more complex applications that are easier to debug, easier to maintain, and meet user requirements more closely than ever before.

Acknowledgements

This article is excerpted from The Visual FoxPro 3 Codebook, by Y. Alan Griver, ISBN 0-7821-1648-5, 1995 SYBEX Inc., with the permission of SYBEX Inc. All rights reserved. We acknowledge the help of Flash Creative Management, Inc., Hackensack, NJ, in providing this material.