Programmatic Manipulation of VFP Class Libraries

Charles Thomas Blankenship

Programmatically manipulating VCX files opens up a whole new world of Automation possibilities. In this article, Charles Thomas Blankenship explains how to add method code to and modify the properties of objects directly within the VFP class library (VCX file) itself.

I don't know about you, but I've grown weary of the tedium associated with building VFP applications from scratch. Even when using productivity frameworks like Mere Mortals and Visual FoxExpress, there's a ton of highly repetitive work to do simply creating business objects, views, interface objects, proxy lists, forms and menus, and so on. After developing dozens of Codebook-based applications, I finally succumbed and wrote an enhancement to the Mere Mortals framework that allows me to place GenScreenX-type drivers in the comment fields of my XCase model (tables and fields), press a button, and have all of these application-level objects created for me automatically. In order to complete this task, it became necessary to learn how to programmatically create new subclasses, and insert method code into as well as manipulate the property values of the newly created class definitions.

The basic process required to accomplish this task is as follows: 1) create an instance of the new class; 2) modify its properties; 3) insert method code; and 4) save the new class—unfortunately, not necessarily in that rather intuitive order. Throughout this article, I use a TextBox interface control as the sample object since it's probably one of the most frequently used controls when building VFP applications.

Creating the new interface object

The first step in this process is to create an instance for a text box whose purpose is to display a person's first name. For this example, let's assume the following:

1.The name of the column that stores the person's first name is v_Person.cPersonFirstName.

2.The class definitions for all interface controls are stored in a class library named aInterfaceControls.VCX.

3.The name of the control is txtPersonFirstName.

4.The name of the superclass used to create the new text box is ATextBox.

The following code creates the instance of the new interface object:

LOCAL loTemplateObject

loTemplateObject = CREATEOBJECT("AtextBox")

Notice that simply by using the ATextBox class definition to create the template object, the parent class of the new object is automatically defined.

Modifying the new object's properties

Now, loTemplateObject contains a reference to what will become the txt_PersonFirstName application interface object. Modifying this object's properties to change its name and define its control source is as simple as this:

loTemplateObject.Name = 'txt_PersonFirstName'

loTemplateObject.ControlSource = 'v_Person.cPersonFirstName'

Or is it? Notice that the attempt to modify the ControlSource property results in the Visual FoxPro error depicted in Figure 1.

Figure 1. Alias Not Found error.

Due to the fact that loTemplateObject is an active object (we're doing this programmatically—in other words, it's live, not Memorex), it's not possible to enter an invalid control source value at runtime. The v_Person.cPersonFirstName value is invalid because, at the time of the assignment, the v_Person view isn't active in the data session. This restriction also applies to all other properties of the object that are read-only at runtime but are read/write at design-time. So what to do? First, I have to save the new object to a class library file. Then I can directly manipulate the properties and methods in the class library file itself.

Saving the new object (creating a new class definition)

Saving the new object, which in effect creates the new object's class definition, is accomplished by using the object's own SaveAsClass() method. The syntax for this method is as follows:

Object.SaveAsClass( ClassLibName, ClassName ;

[, Description] )

where:

ClassLibName

Specifies the name of the .vcx file in which to store

the class definition.

ClassName

Specifies the name assigned to the class.

Description

Specifies the optional description for the class.

The code that creates the new interface object looks like this:

loTemplateObject.SaveAsClass("aInterfaceControls.VCX",;

"txt_PersonFirstName",;

"The person's first name" )

Checking the disk drive reveals that the SaveAsClass() method created a new class library file by the name of aInterfaceControls.VCX. It's in this file that the last step in the process takes place.

Programmatically updating properties in the VCX

As just about everyone knows by now, Visual FoxPro class library files are really VFP tables with a .VCX extension. Opening them is as simple as employing the USE command with the fully qualified filename. The following command:

USE aInterfaceControls.VCX

opens the newly created class library. A simple BROWSE command reveals the table structure shown in Figure 2.

Figure 2. Browse window of the aInterfaceControls.VCX class library.

Now what I want to do is programmatically modify the object's properties. Before any manipulation can take place, however, I have to find the record that contains the txt_PersonFirstName class definition. The following command accomplishes this task:

LOCATE FOR UPPER("txt_PersonFirstName") $ ;

UPPER(ainterfacecontrols.objname) AND ;

.NOT. EMPTY( timestamp )

Now that I've located the proper record in the class library file that contains the class definition for txt_PersonFirstName, I can begin modifying it. Its contents are shown in Table 1.

Table 1. The contents of the record in the class library file that contains the class definition for txt_PersonFirstName.

Field / Value / Meaning
Class / atextbox / The name of the superclass used to create the object.
ClassLoc / libs\acontrols.vcx / The location and name of the superclass's class library.
BaseClass / textbox / The name of the VFP BaseClass used to create the object.
ObjName / txt_personfirstname / The name of the newly created object.
Properties / Name = "txt_PersonFirstName" / The name and value of any properties that contain a non-default value.
Methods / <empty> - not for long / Any method code associated with the object.

Notice that the Name property in the class library's Properties column is represented exactly how it's spelled in the property sheet (see Figure 3). Therefore, providing a non-default value for the ControlSource property is as simple as follows:

lcCurrent = ALLTRIM( aInterfaceControls.Properties)

lcCurrent = [ControlSource = "v_Person.cPersonFirstName"] ;

+ CHR(13) + lcCurrent

REPLACE aInterfaceControls.Properties WITH lcCurrent

Figure 3. Nondefault property sheet for txt_PersonFirstName.

A quick look at the property sheet for this object now verifies that the preceding code has properly populated the ControlSource property. In order for this to work, I must make sure that the spelling of the property is correct. The best way to accomplish this is to manually populate the object's property first and then check the class library file to verify how VFP enters the name of the property itself... and follow suit in my own program (see Figure 4).

Figure 4. Nondefault property sheet for txt_PersonFirstName with ControlSource populated.

Programmatically updating methods in the VCX

One would think that providing method code for the object is as simple as the properties. The answer is yes and no. Simply programmatically placing the following code in the Methods column unfortunately doesn't yield the desired result when viewing the object's property sheet.

PROCEDURE Init

THIS.AddViewParameter( "vp_iID", ;

"vp_iID = v_PersonProxy.iID", "v_Person" )

ENDPROC

The results of this code vary. Sometimes I see a truncated mess; other times I don't see any method code at all in the property sheet. To fix this problem, recompile the class library after clearing it. The following code ensures that I see the method code in the property sheet just as I'd expect to see if I entered the code into the method manually via VFP's IDE.

CLEAR CLASSLIB aInterfaceControls.VCX

COMPILE CLASSLIB aInterfaceControls.

Take the time to notice that the method code located in the Methods column is delimited by the PROCEDURE ... ENDPROC command. This is how VFP delimits one block of method code from another. I have to strictly adhere to this format so that my programmatically generated method code appears as it should in the property sheet.

Conclusion

So there you have it. It's possible and quite easy to programmatically create visual class definitions, populate their properties with non-default values, and enter code into their methods. This technique has reduced the drudgery of creating interface controls and other application layer objects by programmatically identifying their superclasses and populating their properties and methods with derivable information. You're limited only by your imagination.

Charles Thomas Blankenship is the director of software development at Claims Verification Inc., located in beautiful, sunny, and warm southern Florida. He's a misplaced ex-Flasher (the now-defunct Flash Creative Management type) who continually laments the fact that the best company in the world for which to work in the capacity of consultant is now gone forever, along with some great bosses (Yair Alan Griver and David Blumenthal). .