Special Edition Using Visual FoxPro 6


Chapter 16

Managing Classes with Visual FoxPro


What's the big deal about managing classes? You have VCX files with classes in them. What can be so difficult? Well, as it turns out, managing class libraries is not something to take lightly. Without proper management, disaster can easily strike not only one application, but an entire department or even a company.

In Chapter 15, "Creating Classes with Visual FoxPro," you learned about application frameworks. An application framework can consist of hundreds of classes on which you can design other classes for your applications.

In order to properly keep track of the classes, you will probably want to keep them grouped in a logical manner in different files. For example, the base classes (such as NoNotifyTextBox, discussed in Chapter 15) in the framework might be stored in a .VCX file called CONTROLS.VCX. Another .VCX file might have a more specialized version of these controls (such as the OKButton and OKButtonWithToolTip classes discussed in Chapter 15). Yet a third .VCX file might have combination classes, such as the Navigator toolbar. There might be a UTILS.VCX file as well that has ObjectInspectorForm.

When you put all these disparate class libraries together, you have a basis for creating all your applications. Now, imagine what might happen if someone modified the TextBox base class and introduced a bug into it. Conceivably, every application in the company could up and die right there. Sound farfetched? Believe it.

Recently I was speaking to a user group in New York, and we got into the issue of managing class libraries. One of the attendees, who programmed in a mainframe environment (and you know how tightly a mainframe environment is controlled), told me that an application he had just modified and tested began dying shortly after he updated the production software. Immediately, the company reverted to the prior version of the software, but still the problems persisted. This set off two weeks of hectic work trying to track down the cause of the problem, but all efforts failed. Finally, as he was almost ready to give up, he bumped into a friend in the company cafeteria. After his friend remarked that he had not seen him for several weeks, the attendee explained that he had been going nuts trying to track down this problem. His friend turned red. It seems he had modified one line of code in a base routine and never told anyone-he had inadvertently introduced this bug.

The problem illustrated in this story can become even more acute in OOP. Objects, which are self-contained, are not meant to be reviewed and understood by everyone in depth. All a user needs to know is how to interact with them. If something high in the class hierarchy is accidentally broken, hundreds of classes can go south for the winter and no one will understand what happened. The key, therefore, is to ensure proper control over the class libraries.

Class Library Structure

In a typical organization, you might see as many as three levels of class libraries. The first level is a corporate-wide set of standard libraries. For example, the corporate framework and standard utilities might be part of these libraries. At the second level, a specific department or business unit within a company might have a more specialized set of classes used across their applications. For example, a set of base business classes could be created for each business unit. Finally, at the third level, each application has class libraries specifically for its own use. Figure 16.1 shows what the hierarchy would look like in terms of an organization chart.

Figure 16.1 : The class library structure.

Each level of classes requires an individual responsible for managing the libraries. The generic term for such an individual is the class librarian.

The Class Librarian

The class librarian's job is to control what goes into the class libraries. At the corporate level, a corporate class librarian is responsible for the content and integrity of the corporate framework. Each department has similar responsibilities. The leader of each application project, or someone he or she designates, does the same for the application libraries.

Criteria for Inclusion in Class Libraries

It would be unfair to assume that the class libraries at any level should be static and not subject to change. First of all, as time goes by, enhancements can and should be introduced. Developers will work on applications and might have significant contributions to make to the frameworks, in terms of new and enhanced classes. No one is suggesting that this should not take place, but rather that it should be controlled.

Classes that are created for inclusion in the class libraries at any of the three levels are created by the developers and stored in temporary .VCX files. When the developer is done with the classes and is satisfied with their completion, the classes are submitted to the appropriate class librarian for consideration.

The class librarian should carefully review each class for the following items:

Merging the New Classes

After the submitted classes have been reviewed and approved, the next step is to merge the classes into the class libraries. This should take place at regular intervals-once a week or once a month. A rapidly changing class library is difficult on developers because it removes the important element of stability. In addition, once merging is complete, the class librarian should inform all the class libraries' users about the changes and additions.

As a good rule of thumb, never make a change to the class libraries without telling everyone about it. That way, if something does accidentally break, the developer will have a good idea where to look.

Technical Aspects of Managing Class Libraries

On the technical side, there are three basic actions a librarian normally has to take regarding the management of classes in .VCX files:

Visual FoxPro has commands for each of these tasks.

Copying a Class

The ADD CLASS command is used to copy a class from one visual class library file to another. Here's the syntax:

ADD CLASS <ClassName>;
    [OF <ClassLibrary1>] ;
    TO <ClassLibrary2> ;
    [OVERWRITE]

ClassName is the name of an existing class. The class can be a class already accessible in a VCX file loaded with SET CLASSLIB, or it can be one you specifically reference from <ClassLibrary1> with the optional OF <ClassLibrary1> clause. The name of the VCX file in which to store the class is specified with the TO <ClassLibrary2> clause.

If the class already exists in <ClassLibrary2>, an error is generated by Visual FoxPro to prevent the accidental overwriting of a class in a class library. The OVERWRITE keyword overrides this message and automatically overwrites the existing class.

Removing a Class

The REMOVE CLASS command removes a class from a class library. Here is the syntax:

REMOVE CLASS <ClassName> OF <ClassLibrary>

ClassName is the name of the class to remove, and ClassLibrary is the name of the VCX file from which the class is removed.

CAUTION
Removing a class is very dangerous because it can break the chain in a class hierarchy. For example, if you have a class hierarchy and remove one of the classes in the middle of the hierarchy, you will invalidate everything from the class you deleted on down. You will not be able to even edit those classes. As far as native Visual FoxPro is concerned, the classes are toast. Period.
So, what do you do if you accidentally remove a class? The simple answer is to open the .VCX file as a table and recall (using the RECALL command) the records you just deleted. Visual FoxPro does not have a command for deleting a level from a class hierarchy. You can do it with the Class Browser's redefine feature, which is covered later in this chapter.

Moving a Class Between Class Libraries

There is no MOVE CLASS command. In order to move a class, you need to copy it into the new class library and then remove it from the original one.

Renaming a Class

You can change a class in a visual class library file using the RENAME CLASS command. Here is the syntax:

RENAME CLASS <OldClassName> ;
    OF <ClassLibrary> ;
    TO <NewClassName>

OldClassName is the class name as it exists in the VCX file prior to the renaming. ClassLibrary is the name of the VCX file, and NewClassName is the new name for the class.

CAUTION
Be very careful with this command. If you rename a class, it only affects the classes open in the Class Browser. Therefore, any classes subclassed from this class that are not currently open in an open instance of the Class Browser will no longer be able to find it. Starting with Visual FoxPro 5.0, the Class Browser updates references to a renamed class in all associated subclasses that are open in Class Browser windows.

TIP
How would you successfully rename a class that has subclasses if the subclasses were not open in the Class Browser when the class was renamed? The easiest way is to open all subclasses in the Class Browser before you rename a class. If you forgot to do that when you renamed a file, you will have to modify the .VCX file. This may seem scary, but actually it is pretty easy. A .VCX file is a table and can be opened with the USE command. Open the .VCX file, look for instances of the old class name in the CLASS field, and manually change them to the new class name.
I know this sounds scary, but I have done this many times successfully. If you're worried, back up the old class library first. Actually, come to think of it, you should back up the old class library anyway. Paranoia is definitely your friend here.
If you missed a record in the .VCX, don't worry. When you try to edit a class based on the renamed class that has not had the parent class name changed, FoxPro will return an error. You can cancel at that point, find the offending name in the class field, and fix it before moving on.
Finally, if you use the class in a form or container, that container will be affected as well and will not be editable until the appropriate change is made.

TIP
If you don't like the idea of opening the .VCX file manually and doing batch updates, there is another alternative. Using the Class Browser's Redefine feature, you can manually redefine each class to work off the newly renamed class. You can only do it one class at a time, but it will work.
The other alternative is to create an add-in for the Class Browser so that it can go through the class tree and handle all the work for you. The Class Browser is covered after the next section. Look for an add-in to take care of this issue for you.

Managing Classes Visually

Visual FoxPro gives you the commands to perform the rudimentary functions associated with managing class libraries. Add, Remove, and Rename basically cover the bulk of what you want to do.

On the other hand, they aren't very pretty. After you've seen the fancy Form Designer and Visual Class Designer, you would probably be disappointed if you had to manage class libraries by hand. Fortunately, you don't. That's where the Class Browser comes in.

The Class Browser

The Class Browser is a Visual FoxPro application (written entirely in Visual FoxPro) that's designed to provide a user-friendly, visual way to work with and manage class libraries. Loading a class library into the Class Browser provides all the functionality you have learned (and much more) at the touch of a mouse button.

There is one interesting note about the Class Browser. When I think of the Class Browser, I think of it for managing classes. However, because the file structures of .SCX and .VCX files are identical, the Class Browser also enables you to load .SCX files. This is really nice because most (but not all) of the Class Browser's functionality works equally well with a .SCX-based form as it does with a .VCX file. Figure 16.2 shows you what the Class Browser looks like.

Figure 16.2 : The Class Browser with its Shortcut menu open.

The interface is very clean. The following sections explain the features presented in the Class Browser.

Form Caption

The title of the Class Browser form shows the name of the currently selected class and the name of the .VCX or .SCX file to which the selected class belongs.

Action Buttons

At the top of this modeless form is a series of command buttons that launch the bulk of the Class Browser's actions. Here is a list of them:

Button Purpose
Component Gallery Toggles between the Component Gallery and the Class Browser. The Component Gallery is described in Chapter 19, "The Visual FoxPro Component Gallery."
Open Opens a class library. Any open class libraries are closed when the new one is opened.
View Additional File Opens an additional class library and shows it with the current one. This is useful when the parent class of a class is stored in a different .VCX file.
View Class Code Exports the code for the currently selected class and shows it in a window. If the currently selected class is the .VCX file itself, the entire class library is exported all at once.
Find Finds text within a class in the open libraries.
New Class Creates a new class based on the currently selected class. To create a new class in a .VCX file based on a Visual FoxPro base class, position the list on the .VCX file item and then click the New Class button.
Rename Renames the currently selected class.
Redefine Changes the parent class of the current class.
Remove Deletes the currently highlighted class.
Clean Up Class Library Packs the .VCX file associated with the currently selected class.
Add-Ins Runs an installed add-in. Add-in buttons are placed at the right side of the toolbar. However, none are present in Figure 16.2.

As you can see, there is a lot here. The following sections explain the functions of the command buttons.

If you right-click on a member in the Member list (right panel), a shortcut menu opens, as shown in Figure 16.2. You can also right-click on a class in the Class list (left panel) or the Class Browser toolbar. The shortcut menu is context-sensitive, and the Class Browser displays only those menu items that are relevant to the object on which you've right-clicked. Here is a list of the shortcut menu items:

Button Purpose
Descriptions Toggles on (checked) and off the display of class library and class descriptions at the bottom of the screen. The Class and Members Description panels are displayed only if this option is checked.
Always on Top When checked, the Class Browser stays on top all the time.
Auto Expand When checked, the class list expands or collapses automatically when it refreshes.
Parent Class Toolbar When checked, a button is placed on the Visual FoxPro Systems toolbar that you can click on to edit the ParentClass's method.
Restore Defaults Restores Class Browser settings to their defaults, which are as follows:
Descriptions
   Always on Top
Auto Expand
Hierarchical
   Protected
   Hidden
   Empty Filter
Hierarchical When checked, classes are displayed in a hierarchy. Subclasses are displayed beneath parent classes. When not checked, classes are displayed in alphabetic order.
Protected When checked, the Class Browser displays protected members in the Members list.
Hidden When checked, the Class Browser displays hidden members in the Members list.
Empty When checked, the Class Browser displays empty methods in the Members list.
Modify Launches the Class Designer to modify the selected class or launch the Form Designer to modify a form.
Rename Renames the selected class, property, or member. For classes, the Class Browser renames all references to a renamed class in all files open in all instances of the Class Browser.
Redefine Selects a new ParentClass for the selected class.
Remove Removes a selected class.
View Views the current value for a selected property.
Font Chooses a font attribute for all of the controls in the Class Browser.
Export Saves to a file and displays the class definition, values of the properties, and method code for the selected class.
Container Icon Selects an icon for the selected class.
Select ParentClass Selects the parent class of the selected class. If the Class Browser does not already display the ParentClass, the visual class library containing the ParentClass is added to the Class Browser.
New Window Creates a new incidence of the Class Browser Window.
New Component Gallery Creates a new Component Gallery window.
Refresh (F5) Refreshes the Class Browser display.
Add-ins Selects an add-in to place on the toolbar from a menu of all registered add-in programs, classes, and applications.
Help Launches Visual FoxPro Help for the Class Browser.

Open  This button opens a file; both .VCX and .SCX files can be opened. Visual FoxPro showsa GetFile() dialog box with the list of .VCX files in the current directory (actually, the last directory GetFile() looked to, but that's another story). From this dialog box, you select a file. To select a .SCX file, change the file filter at the bottom of the dialog box. Figure 16.3 shows the dialog box with .SCX files.

Figure 16.3 : The Class Browser GetFile() dialog box, showing .SCX files.

Selecting Cancel aborts the request to open a file. Also, when you open a new file, all files already opened in the Class Browser are closed.

View Additional File  This button enables you to show an additional file in the Class Browser. This is extremely useful when files are stored in more than one .VCX file.

View Class Code  This button generates code for the currently selected class and shows the code in a window. If a file is selected in the class list, all classes in the file are generated and shown.

For the most part, the code generated will run if it is pasted into a .PRG file, but there are limitations to this. For example, if you have a container on a form (such as a page frame) with objects added to the class, the code presented in Listing 16.1 is generated by the Class Browser and will not run. Figure 16.4 shows what the form looks like in the designer.

Figure 16.4 : The WontRun form.


Listing 16.1  16CODE01-Example of a Program Generated by the Class Browser Export Operation That Will Not Run
**************************************************
*-- Form:         frmwontrun (d:\seufpw6\chap16\wontrun.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Notes:        Exported code from Class Browser
DEFINE CLASS frmwontrun AS form
DoCreate = .T.
Caption = "Special Edition Using Visual FoxPro 6"
Name = "frmwontrun"
ADD OBJECT pageframe1 AS pageframe WITH ;
ErasePage = .T., ;
        PageCount = 2, ;
        Top = 24, ;
        Left = 24, ;
        Width = 325, ;
        Height = 173, ;
        Name = "Pageframe1", ;
        Page1.Caption = "Page1", ;
        Page1.Name = "Page1", ;
        Page2.Caption = "Page2", ;
        Page2.Name = "Page2"


    ADD OBJECT frmwontrun.pageframe1.page1.command1 AS commandbutton WITH ;
        Top = 44, ;
        Left = 143, ;
        Height = 73, ;
        Width = 133, ;
        Caption = "Command1", ;
        Name = "Command1"


    ADD OBJECT label1 AS label WITH ;
        FontBold = .T., ;
        FontSize = 24, ;
        Caption = "Visual FoxPro 6.0", ;
        Height = 48, ;
        Left = 43, ;
        Top = 204, ;
        Width = 288, ;
        Name = "Label1"


ENDDEFINE
*
*-- EndDefine: frmwontrun
**************************************************

Note that this form will run fine with a DO FORM command. Running it as a .PRG file doesn't work-it will bomb when it attempts to the add the command button to the page frame.

Find  This button presents a dialog box (see Figure 16.5) where you enter the text you want to find. The text is first searched for in the names of the classes, then in the class descriptions, and finally in the descriptions of the custom methods and properties.

Figure 16.5 : The Find dialog box.

New Class  This button creates a new class. If the currently selected class is actually a filename, the Class Browser assumes that the parent class is a base class and displays the New Class dialog box accordingly (see Figure 16.6). Notice how the name of the file where the new class is stored is automatically filled in. The Store In object on the dialog box defaults to the name of the .VCX where the currently selected class is stored.

Figure 16.6 : The New Class dialog box, based on a base class.

If a class is selected in the class list, the Class Browser assumes that the new class is a subclass of the currently selected class. Accordingly, all the fields in the dialog box are automatically filled in. All you have to supply is a class name (see Figure 16.7).

Figure 16.7 : A new subclass.

Rename  This button enables you to rename a class in the dialog box that is displayed (see Figure 16.8).

Figure 16.8 : The Rename dialog box.

CAUTION
Just a friendly reminder: The caution I gave you for the RENAME CLASS command in the section "Renaming a Class" applies here too.

Redefine  This button or shortcut menu item enables you to change the parent class of the currently selected class. This function is available only in the Class Browser, by the way. Here's how it works.

The Redefine dialog box is displayed (see Figure 16.9). In order to redefine a class as a subclass of its Visual FoxPro base class, leave the fields blank and select Redefine. Otherwise, type in the name of the class to be the new parent class. Note that the dialog box automatically assumes that the new parent class is in the same class library as the class you are redefining. If it isn't, you will have to either type in the name of the class library or click on the command button with the ellipsis on it to get a GetFile() dialog box to select the .VCX file.

Figure 16.9 : The Redefine dialog box.

There is one restriction on this function. The new parent class must have the same base class as the prior one. For example, you can't redefine a CommandButton-based class as a CheckBox-based class.

Remove  The Remove button or shortcut menu item deletes the currently selected class. The Class Browser gives you a warning before it deletes the class.

CAUTION
The caution I gave about REMOVE CLASS in the section "Removing a Class" applies here, too.

Clean Up Class Library  This option packs the .VCX file.

Add-Ins  This option displays a list of add-ins. See the section "Putting It All Together with Add-Ins" later in this chapter for more information.

Type Filter

If you have multiple .VCX files open or have a lot of classes in one file, it may be difficult to find classes in the list. The Type filter at the left of the toolbar enables you to set a filter on the type of class in the list. The dropdown list box shows the list of Visual FoxPro base classes (CommandButton, ToolBar, and so on). As an alternative, you can type in class names to filter the list. Figure 16.10 shows the Class Browser with a filter applied.

Figure 16.10: The Class Browser with a filter applied.

The Type filter fully supports wildcards. For example, to find every class with "Button" in the name, you could specify %Dial% (case doesn't matter). Button* shows all classes with names beginning with "Button". The question mark (?) is a single-character wildcard.

If you type in your own filters, they are automatically added to the filter list for that session.

Display Method

The Class Browser can show the classes in hierarchical order (the default order) or in alphabetical order. Hierarchical order shows the classes in tree form, with subclasses beneath parent classes. Alphabetical order ignores the class hierarchy and just shows all the listed classes in alphabetical order. Figure 16.11 shows the Class Browser in alphabetical order.

Figure 16.11: The class list in alphabetical order.

Protected Member

By default, the Class Browser will not list protected properties and methods when a class is displayed. If you select the Protected shortcut menu item, the protected members are displayed at the appropriate position in the member's list. If this menu option is selected, a check mark appears to the left of the Protected Member menu item. If a property or method is protected, it is shown in the Members list and is preceded with an asterisk (*), and a protected icon (a key) appears next to the member's icon. See Figure 16.11 for examples of protected members.

Empty Method

By default, custom methods (methods not inherited from prior classes) that are attached to a class will be displayed only if there is code in them. If you select the Empty shortcut menu item, the empty methods are displayed at the appropriate place in the Members list. If this menu option is selected, a check mark appears to the left of the Empty Method menu item.

If an empty method is shown in the Members list, the method's name is preceded by a tilde (~).

Hidden Members

By default, the Class Browser does not display hidden properties or methods in the Members list. A hidden member can not be accessed or modified outside of its associated class. If you select the Hidden shortcut menu item, the hidden members are displayed at the appropriate place in the Members list. If this menu option is selected, a check mark appears to the left of the Empty Method menu item.

If an empty method is shown in the Members list, the member's name is preceded by a tilde and a hidden icon (a lock) is displayed beside the member's icon in the member list. You can see examples of hidden members in Figure 16.11.

Class Icon

The Class icon is the picture just above the left corner of the list of classes. It's a visual representation of the class currently selected in the list. You can set the icon in the Class Information screens by right-clicking on it. This is discussed in Chapter 15.

The icon is more than a picture, though. It is also the Drag/Drop button. Clicking and dragging this icon to a form in design mode will automatically add that class to the form. It is functionally equivalent to loading the class library into the Form Controls toolbar and dropping the class library on the form.

But there's more. By dropping the icon on the desktop, you attempt to instantiate and run the class right then and there. This can be a cool way to test your classes, especially for most form classes.

Finally, you can use drag-and-drop to move or copy classes from one .VCX file to another. Here's how you do it. Open the Class Browser twice (once for each .VCX file). To open a new Class Browser instance, choose the New Window menu item from the shortcut menu. Then open another visual class library. Grab the Class icon that you want to move and drag it to the other Class Browser instance, as shown in Figure 16.12. If you want to copy the class, press the Ctrl key while you drag the Copy icon. Be careful, and watch the drag icon to make sure that it changes to the Move or Copy icon before you drop the class. The Copy icon is like the Move icon, except it contains a plus sign (+). When you drop the class, it will be added to the second Class Browser instance.

Figure 16.12: The Class Browser with the Class icon.

List of Classes

The Class list is in the left panel of the Class Browser. This list is based on the outline control that ships with Visual FoxPro. When the Class list is in hierarchical mode, you can expand or contract a branch of classes by clicking the + or - icons.

Double-clicking the class name loads the class into the Visual Class Designer for editing. Double-clicking the folder moves to the parent class for that class.

If a class is based on a class in another .VCX file that is not shown, a chevron (<<) appears between the folder and the class name. (See Figure 16.13.) If you choose the Show Parent Class item from the shortcut menu, the Class Browser automatically loads the .VCX file with the parent classes before moving to the parent class, as shown in Figure 16.14.

Figure 16.13: A class based on another .VCX class.

Figure 16.14: The Class Browser with a parent .VCX file loaded.

Members List

The right panel of the Class Browser shows the members of the currently selected class as a hierarchy, with a tree branch for Object Members, Methods, and Properties.

Class Description

This edit box enables you to view and edit the description for a class. The description entered in this box is automatically placed in the .VCX file and appears in the Description box of the Class Info dialog box. This is particularly nice because it is not uncommon to add some descriptions at the end of the process, and this saves having to go into the Visual Class Designer to do so.

Member Description

This edit box is the description of a selected member in the Member lists. For example, if an object member is selected, the edit box will show the class and base class names of the object member. For methods and properties, the edit box is read/write and has the description for the property or method entered in the New Property/Method or Edit Property/Method dialog box. Because this edit box is read/write, you can enter or edit the description in the Class Browser and have the description carry forward to the .VCX file. Finally, if an instance variable is selected, the edit box is read-only and shows information about the instance.

Starting the Class Browser

You can start the Class Browser in one of three ways. One way is to use the menu by selecting Tools, Class Browser. A dialog box appears and asks you for the name of the class library to load. If you type in the name of a class library that does not exist, the Class Browser creates it for you.

The second way to load the Class Browser is to use the _browser system variable. _browser is typically set to BROWSER.APP, which is located in the Visual FoxPro home directory. Issuing the command

DO (_browser)

is equivalent to using the menus. In addition, you can pass a parameter through with the name of the class library to load. The following example will load ABOUT.VCX for the TASTRADE sample application:

DO (_browser) WITH HOME()+"samples\mainsamp\libs\about"

Optionally, you can add a second parameter to the command that will automatically select a particular class from the list. The following example will load the About library and select the Aboutbox class:

DO (_browser) WITH HOME()+"samples\mainsamp\libs\about", ;
    "aboutbox"

The next example will start the Class Browser with the form Customer.SCX loaded:

DO (_browser) WITH HOME()+"samples\Tastrade\Forms\customer.scx"

The Class Browser and .SCX Files

TIP
One neat feature of the Class Browser shows an SCX file in the hierarchy with its parent class.
Visual FoxPro enables you to use the Tool, Options menu item on the Forms tab to specify what the form template class is (see Figure 16.15). The template form is the form used as the base class when a new form is created with CREATE FORM.

Figure 16.15: Specifying DefaultForm.

Suppose you have a form class called DefaultForm and you create a form with CREATE FORM. The form I created to illustrate this is called FromDef. Figure 16.16 shows the form in the Form Designer.

Figure 16.16: FromDef.SCX in the Form Designer.

Now you can bring up the form in the Class Browser, as shown in Figure 16.17. For the record, I have also added the .VCX file with the DefaultForm class in it. Although FormDef is an .SCX-based form, it shows up in the proper place in the class hierarchy in the Class Browser class list.

Figure 16.17: Class Browser with FormDef.SCX.

There's more. Once you get a view like this one going, you can even change your form's parent class (that is, its super class) with the Redefine function. Cool stuff.

Under the Hood with the Class Browser

The next step in working with the Class Browser is to understand the methods, properties, and object members that make up the class. Why, you ask? One of the nicest features of the Class Browser is that, as an object, it is totally open and extensible. You can manually call methods from outside the Class Browser. Here is an example:

_obrowser.cmdexport.click()

This example runs the Click() method behind the Export item in the shortcut menu, and brings up the code for the currently selected class as if the button had been clicked.

Being able to access the methods and properties of the Class Browser is important for two reasons. First of all, it enables you to create programs that can interact with the Class Browser in all kinds of ways. The public methods and properties of the Class Browser and all its members are open for you to work with at your pleasure. The second reason is that the Class Browser supports add-ins, which can be hooked to objects and methods in the Class Browser. You'll learn about add-ins later in this chapter in the section "Putting It All Together with Add-Ins."

The point here, however, is that in order to use these powerful capabilities, you have to know what goes on under the hood of the Class Browser.

By the way, _oBrowser is the name of a system memory variable that references the Class Browser when it is up (otherwise it is .NULL.). This is typically the way in which you would access the Class Browser's properties and methods (as opposed to using _screen.activeform). If you find the name lengthy, you can always assign it to another, shorter variable name.

The next step: the object members, methods, and properties of the Class Browser.

Object Members

The object members listed here are all named with standard naming conventions. For example, an object with a name starting with CMD is a command button.

Object Name Class Browser Object
cmdOpen Open command button
cmdAdd Add command button
cmdExport View Class Definition Code command button
cmdFind Find command button
cmdSubclass Add Subclass command button
cmdRename Rename command button
cmdRedefine Redefine command button
cmdRemove Remove command button
cmdCleanup Clean Up Library command button
opgDisplayMode Display Mode option group
optHierarchical Hierarchical option button
optAlphabetical Alphabetical option button
chkProtected Protected check box
chkEmpty Empty check box
imgClassIcon Class Icon image
cmdClassIcon Class Icon command button
lblClassType Type label for the Type combo box
cboClassType Type combo box
txtClassList3D Class List 3D effects control
oleClassList Class List Outline OLE container
txtMembers3D Members List 3D effects control
oleMembers Members List Outline OLE container
lstMembers Objects Member listbox
lstClassList Methods list box
edtClassDesc Class Description edit box
edtMemberDesc Member Description edit box

The properties and methods of these objects are available for your use. As you saw earlier, you can call the methods with ease. You can also change the properties of these objects. For example, you could change the ToolTipText property of the cmdFind button as follows:

_oBrowser.cmdFind.ToolTipText = "Find that darn class!"

When this line of code executes, the Export Code ToolTip will be displayed when the mouse button hovers over the View Class Code button (as shown in Figure 16.18).

Figure 16.18: The Class Browser showing new tooltip text.

You can also hide an object by setting its visible property to .F.. This is a way to temporarily "remove" an object from the Class Browser form. Keep this one in mind, because I'll come back to it.

Methods

The Class Browser supports all the methods in the Form class (on which it is based). In addition, the following methods are supported.

AddClass()  This method copies a class from one class library (.VCX) file to another. AddClass() is used after a drag-and-drop operation between instances of the Class Browser. If the specified class does not exist, a class is created.
AddFile([<VCXFileName>])  This method adds a .VCX file to the Class Browser class list. It is used by the cmdAdd button to add a file to the Class Browser. If no parameters are passed, an AddFile dialog box appears by using the Class Browser GetFile method.

VCXFileName is the name of the file to add. It can be a .VCX file (which is the assumed extension) or an .SCX file (if you specify .SCX in the filename).

AddIn()  This method registers or removes an add-in in the Class Browser. See "Putting It All Together with Add-Ins" for a discussion of add-ins in the Class Browser, what they mean and how they can help you, and what methods the Class Browser has to support them.
AddInMenu()  This method displays the menu of installed add-ins. See the section on add-ins.
AddInMethod()  This method runs a method associated with an add-in. See the section on add-ins.
AutoRefresh()  This method determines whether the current class needs to be refreshed, and automatically refreshes the class if needed (that is, the list of members is automatically rebuilt).
BinToInt(cBinaryValue)  This method returns an integer value resulting from the conversion of a string containing a binary number. For example, a string containing "0101" would return 5.
CleanUpFile()  This method packs the .VCX file associated with the currently selected class.
ClearBrowser()  This method is for internal use only.
ClearClass(<lClearAll>)  This method releases the cache of classes from memory. It executes the Visual FoxPro CLEAR CLASS command for a designated class. Or, if the parameter, lClearAll, is true (.T.), it releases all of the classes from memory.
DisplayMenu(<tnMenuMode>)  This method displays shortcut memory based on the position of the mouse. The value of the tnMenuMode variable determines the shortcut menu that is displayed. Here are the allowable values:

TnMenuMode Shortcut Menu
      0 Class List shortcut menu
      2 Member List shortcut menu

DeactivateMenu()  This method closes the menu of installed add-ins. See the section on add-ins.
DoAddIn([<cAddInName>])  This method executes either the current add-in or the add-in associated with specified record (cAddInName) from the table with the BROWSER alias. See the section on add-ins.
ExportClass([<tlShow>][,<cToFileName>])  This method generates the code for the currently selected class and then returns the text as a string. If the optional logical parameter is sent through as .T., a window will pop up with the code. If the path and filename of the output file is specified, the code is exported to the file.
FileMatch([cFileName][, cFileList])  This method searches the file list (cFileList) for a match on the specified file (cFileName) to find out if the file is associated with an add-in.
FindClass([<tcTextToFind>])  This method looks through the classes loaded in the Class Browser for the text passed. It first searches for the text as the name of a class in the list. If it does not find the text to match a name of a class, it then searches the class description. If it does not find the text in the class description, it then looks in the member names and descriptions for the text.

If the optional parameter <tcTextToFind> is not passed, a dialog box is displayed that enables you to type in the text to find.

FormAddObject(<Object Name> [, <X Coord>] [, <Y Coord>] [, <Activate Form?>] [, <Design Mode?>])  This method adds an instance of the selected class to an external form. Here is what the parameters mean:

Object Name The name of the form to which you want to add the class. The form can be running or a form in design mode.
X Coord This parameter is the horizontal position. It defaults to the current mouse position.
Y Coord This parameter is the vertical position. It defaults to the current mouse position used.
Activate Form? This is a logical parameter. If the parameter is true (.T.), the destination form is activated when the object is added to it. If the parameter is false (.F.), the Class Browser remains active.
Design Mode? This is a logical parameter. If true (.T.), the object reference is an object currently in the Form Designer or Class Designer.

FormAddObject is a useful method that enables you to do some interesting things. For example, consider the form presented in Listing 16.2.


Listing 16.2  16CODE02-Example Form Class Code Exported from the Class Browser
*  Form..............: RUNFORM.SCX
*  Author............: Menachem Bazian, CPA
*  Notes.............: Exported code from Class Browser.

**************************************************
*-- Form:         form1 (runform.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*
DEFINE CLASS form1 AS form

    DoCreate = .T.
    Caption = "Form1"
    Name = "Form1"

    PROCEDURE DragDrop
        LPARAMETERS oSource, nXCoord, nYCoord

        oSource.Parent.FormAddObject(this)
    ENDPROC

ENDDEFINE
*
*-- EndDefine: form1
**************************************************

The code in the DragDrop method accepts a parameter called oSource, which is the object dropped on the form.

With this code, run the form with the Class Browser running. Then take a class from the Class Browser, drag it over the running form, and drop it. The DragDrop event for the form is activated. In this case, oSource represents the oSource property of the Class Browser. Thus, oSource.Parent references the Class Browser itself. Calling FormAddObject and passing it the form as a parameter adds the currently selected class in the Class Browser (that is, the class you dropped onto the running form) to the form.

FormatMethods(<Method Text>)  This method formats the text of generated methods in the display of the class definition code. The text of the methods is passed in the one parameter to the method. This method is designed for internal use, but could be useful if you want to create your own export utility.

FormatProperties(<Properties Text>, lAddObject)  This method formats the text of generated properties in the display of the class definition code (that is, lines with ADD OBJECT and a list of properties set on that command line). The text of the ADD OBJECT command is passed through in the one parameter to the method. This method is designed for internal use, but could be useful if you want to create your own export utility.

GetFile(<File Extensions>)  This method opens the Class Browser GetFile dialog box and is intended for internal use. The difference between the GetFile method and the GetFile function is that the GetFile method will automatically read the cFileExt property of the Class Browser for the list of file extensions to list in the file filter on the GetFile dialog box.

IndentText(<Code>)  This method indents a block of text (passed in the Code parameter) by one tab.

ModifyClass()  This method opens the Visual Class Designer for the currently highlighted class.

MsgBox(<Message> [, <Dialog box Type>] [, <Dialog box Title>])  Based on the MessageBox function, this method brings up a message dialog box. The differences between this method and the MessageBox function are that the icon defaults to the exclamation point, the title defaults to the caption of the Class Browser window, and the buttons are OK and Cancel by default.

NewClass()  This method creates a subclass of the selected class in the class list. To create a new class in a .VCX file based on a Visual FoxPro base class, position the list on the .VCX file item and then run NewClass().

NewFile([<FileName>] [, <Open Automatically?>])  This method creates a new .VCX file. If no parameters are passed, the Open File dialog box is displayed. If the FileName parameter is passed and is a new class library (.VCX file), the .VCX is automatically created (without the dialog box). Specifying .T. to the second parameter will automatically add the new .VCX file to the class list in Class Browser.

OpenFile([<FileName>])  This method opens a file and is the equivalent of the Open button. Passing a filename will open the file. If no filename is passed, an Open dialog box is displayed.

ProperBaseClass(<Base Class Name>)  This method accepts a class name and makes a "proper" name out of it. For example, commandbutton becomes CommandButton. If the class name passed through is not the name of a FoxPro base class, the method capitalizes it (for example, mycommandbutton becomes Mycommandbutton).

RedefineClass([<New Parent Class>] [, <VCX File>])   RedefineClass redefines the currently selected class to be a subclass of <New Parent Class>, which is contained in <VCX File>.

CAUTION
The same caution regarding the renaming classes discussed in the "Renaming a Class" section applies here.
RefreshButtons()  This method refreshes the Class Browser command buttons based on the file type. Different command buttons are enabled and disabled based on the type of file loaded (.VCX or .SCX file). This method checks the buttons against the file type.

RefreshClassIcon()  This method reloads the class icon from the icon file.

RefreshClassList([<Class to Select>][, <Ignore the table?>])  This method refreshes the class list. The first parameter dictates which class should be selected when the refresh is done. The second rebuilds the list based on the internal arrays and ignores the underlying .VCX file table.

RefreshClassListSubclass()  This method is for internal use only.

RefreshFileAttrib()  The Class Browser has a property called lReadOnly that signifies the read-only attribute of a Visual Class library. (A file can be flagged as read-only in FILER, for example.) This method checks the read-only attribute of the .VCX file and updates lReadOnly based on what it determines.

RefreshCaption()  This method refreshes the caption of the Class Browser form.

RefreshDescriptions()  This method is for internal use only.

RefreshMembers([tcDefaultMember])  This method refreshes the Members list to display the members associated with the selected class in the class list.

RefreshPrefRecNo()  This method refreshes the BROWSER.DBF preference record pointer for the class library (.VCX file) or form (.SCX file) being edited.

RefreshRecNo()  This method refreshes the current record pointer of the class library (.VCX file) or form table (.SCX file) for the file of the selected class.

RemoveClass([<Confirm Before Removal?>])  This method removes the selected class in the class list from its associated class library (.VCX) file. If .T. is passed, the Class Browser will present a warning before removing the class.

CAUTION
By default, this method removes the selected class without a warning. Beware of this. If you accidentally remove a class, it will be deleted from the .VCX file. To recover a class, you will have to open the .VCX file and recall the records manually.

RenameClass(<New Class Name>)  This method changes the class name of the selected class in the class list to the name specified in the parameter. If a name is not passed, a dialog box is presented that enables you to enter the new class name.
CAUTION
The same caution regarding the renaming classes discussed in the "Renaming a Class" section applies here.

SavePreferences()  This method saves current preference settings to the BROWSER.DBF registration table. BROWSER.DBF is a table similar to the FOXUSER file. It saves preferences for every .VCX file opened. The preferences saved include items such as form size, left and top coordinates, and so on. Add-in registrations are also saved in BROWSER.DBF.

ScaleResize()  When the Class Browser form is resized, the controls on it are automatically resized to fit the form. ScaleResize is the method that handles this process.

SeekClass(<Class> [, <VCX File>])  This method moves the list pointer to a specified class. The parameter can be either numeric (that is, the list pointer moves to the class n in the list, where n is the number passed through as the Class parameter) or the name of the class to find.

The VCX File parameter is useful if you have multiple class libraries open and the same class name is in several viewed .VCX files. Specifying the .VCX filename will limit the search to that .VCX file.

The parameters are not case sensitive. The class you search for must be visible in order for it to be found, and the method returns a logical value indicating whether SEEK() was successful.

SeekMember(<tcMember>)  This method repositions the member list pointer to the specified member. The parameter, tcMember, can be either numeric or the name of the member. If the parameter is numeric, the member position pointer is moved to member number tcMember in the list.

SeekParentClass()  This method displays the parent class for the selected class. If the parent class is in a different .VCX file not shown in the list, the associated .VCX file is automatically loaded. This method returns a logical value indicating the success of the search.

SetBusyState(<Is State Busy?>)  Sets the lBusyStatus property to true (.T.) or false (.F.) based on the logical parameter BusyState. If the Class Browser is set to busy (.T.), the mouse pointer is set to an hourglass shape. Otherwise it is set to an arrow. Other than this, SetBusyState does not really do much.

SetFont(<FontName>,<FontSize>)  This method sets the font and font size used by the Class Browser for its form, controls, and dialog boxes. Note that you do not have to pass the font name each time. You could use the following example:

_oBrowser.SetFont(,10)

This sets the Class Browser to a 10-point font size without changing the font itself.

ShowMenu(<Menu Array>[, <Command on Select>])  This method displays a menu based on an array. If a menu is a single-dimension array, this method displays a list of all items in the menu. When an item is selected, the command specified in <command on select> is executed. Here is an example:
DECLARE laMenu2[3]
laMenu2[1] = "IBM"
laMenu2[2] = "Intel"
laMenu2[3] = "Microsoft"
_oBrowser.ShowMenu(@laMenu2, "Wait Window Prompt()")

Wait Window Prompt() is the <command on select> and will execute if any of the items on the menu are selected. As shown in Figure 16.19, the menu is brought up at the location of the mouse pointer.

Figure 16.19: The Class Browser with a menu showing.

If the array is two-dimensional, the first column becomes the list of prompts on the array and the second column is assumed to be the command to execute when the associated menu item is selected. Here is an example:

declare laMenu[3,2]
laMenu[1,1] = "IBM"
laMenu[2,1] = "Intel"
laMenu[3,1] = "Microsoft"
laMenu[1,2] = "WAIT WINDOW 'International Business Machines'"
laMenu[2,2] = "WAIT WINDOW 'Intel Corporation'"
laMenu[3,2] = "WAIT WINDOW 'Microsoft Corporation'"
_oBrowser.ShowMenu(@laMenu2)

If an item is selected, the command in the second column in that array is executed. For example, selecting IBM will execute the WAIT WINDOW 'International Business Machines' command.

TrimExt(<FileName>)  This method accepts a filename as a parameter and returns the name minus the extension. Here is an example:
? _oBrowser.TrimExt("BROWSE.DBF") && Returns "BROWSE"

TrimFile(<FileName with Path>)  This method accepts a filename as a parameter and returns the path only (that is, it trims off the filename).

TrimPath(<FileName With Path>[,<Trim the extension?>])  This method accepts a filename as a parameter and returns the filename and extension only (the path is removed).

Here is an example:

? _oBrowser.TrimPath("D:\APPS\BROWSE.DBF")
* Returns "BROWSE.DBF"

VersionCheck(<Show Error Message?>)  This method validates the open class library (.VCX file) or form (.SCX file) and returns .T. if the file is valid, .F. if it is not. If a parameter of .T. is passed, an invalid file will cause an error message to appear.

How does this method work? It checks the contents of the field Reserved1 for a proper version number. If you want to call this method on your own, make sure that the record pointer in the .VCX file is set to the top of the file (setting the list selection won't work-that works off an array). The .VCX file you are looking for is open in the Class Browser's data session with an alias of Metadata<n>, where n is the number of the .VCX file (the first .VCX file opened is given an alias of metadata1, the second is metadata2, and so on).

You can find which metadata file to work with by checking the DBF file underlying the metadata<n> alias with the DBF function. When DBF matches the name of the current .VCX file (you can get this from the Class Browser's cFileName property), simply issue GO TOP and then run the versioncheck method.

This method runs automatically when the .VCX file is loaded into the Class Browser. You shouldn't have to use this method too often, but it's good to know.

UpdateReference(<tcOldClassLoc>, <tcOldClass>, <tcNewClassLoc>, <tcNewClass> [,<lAllInstances>])  This is a powerful new method that was added to Visual FoxPro Version 6. It updates all class references when a class is renamed, or when a class is moved from one instance of a Class Browser to another. The tcOldClassLoc and tcNewClassLoc arguments refer to the ClassLoc field contents in the .VCX or .SCX file. The tcOldClass and tcNewClass arguments refer to the Class field in the .VCX or .SCX file. The UpdateReference() function changes all files in all Class Browser windows if the tlAllinstances argument is true (.T.). If it's false (.f.), the UpDateReference() function changes only the files in the current Class Browser.

ViewProperty(cProperty)  This method displays the value of the designated property for the selected class in a message box. For example, if you want to know what the caption is for a form class, click on the class and execute the following command from the Visual FoxPro Command window:

_oBrowser.ViewProperty("Caption")

The value of the Caption property is displayed in a message box, as shown in Figure 16.20. Notice that the message box title bar contains the name of the class and its visual class library.

Figure 16.20: ViewProperty() displays a message box showing the value of a property.

WildcardMatch(<Wild Card Expression List>, <Expression to Search>)  This method compares strings for a wildcard match with the filters specified in the Type box. Here is an example:
? ox.Wildcardmatch("ox?", "OxyMoron")            && Returns .F.
? ox.Wildcardmatch("ox?, ox*", "OxyMoron")       && Returns .T.

Properties

Table 16.1 presents the properties of the Class Browser.

TIP
Note that some properties are marked as "No" in the "Can It Be Changed?" column. This does not mean that these properties cannot be changed if you issue the commands to do so. These properties are not protected. However, if you do change these properties, you could cause problem for the Class Browser. It may abend, work improperly, or who knows what all.
Simple lesson: if the chart says "No," it means that you change the property at your own risk.

Table 16.1  Properties of the Class Browser
Property Name
Description
Can It Be Changed?
aClassListAn array with two dimensions that holds information about each class in the class list. Here's the structure of the array:
No
 [Row,1]   The class name.
 
 [Row,2]   The record number in the .VCX file.
 
 [Row,3]   The number of levels indented in the list.
 
 [Row,4]   The .VCX file with the parent class.
 
 [Row,5]   The parent class name.
 
 [Row,6]   The path of the parent class's .VCX file.
 
aFilesAn array listing the files open No in the Class Browser.
 
aInstancesAn array of instances for the selected class in the list.
No
BackColorThe background color of the form.
Yes
cAddInMethodThe name of the method that called the currently executing add-in. See the section on add-ins beginning with "Putting It All Together with Add-Ins."
No
cAliasThe alias of the .VCX file for the current class (for example, METADATA).
 
CaptionThe caption of the window.
Yes
cBaseClassThe name of the base class of the currently selected class in the Class Browser.
No
cBrowserTableThe name and full path to the Class Browser registration table (BROWSER.DBF).
No
cClassThe name of the currently selected class.
No
cClassLibraryThe name of the .VCX file where the parent of the current class is located. This is empty for classes based on a base class.
No
cClassTypeThe current Type filter.
No
cDefaultClassThe name of the initial class to select in the Class Browser (the third parameter on the DO (_browser) command line).
Yes
cDragIconThe name of the icon file used as the picture for the drag icon.
Yes
cLastFindTextInternal use only.
No
cFileNameThe .VCX filename of the currently selected class.
No
cFilterThe filter set on the .VCX file for the current class.
No
cGetFileExtThe extensions shown in the GETFILE() displayed by the Open button.
Yes
cLastSetCompSET("compatible") before the Class Browser loaded.
No
cLastSetESCSET("escape") before the Class Browser loaded.
No
cLastValueFor internal use only.
No
cLastSetUDFParmsFor internal use only.
No
cParentClassThe parent class of the currently selected class.
No
cParentClassBrowserCaptionThe ParentClass Browser Caption.
Yes
CparentClassSymbolSymbol displayed (normally <<) to indicate that class is subclass of a class that is not displayed.
Yes
cPlatformThe current platform (Windows, for example).
No
cProgramNameFull path to BROWSER.APP.
No
cStartNameThe name of the Class Browser before it was incremented by additional instances. (Showing more Class Browsers will increment the name to Classbrowser1, Classbrowser2, and so on.)
No
cTimeStampThe timestamp of the currently selected class. For internal use only.
No
DataSessionThe data session setting for this form. (The Class Browser runs in a private data session.)
No
DoCreateFor internal use only.
No
FontNameDefault form font.
Yes
FontSizeDefault form font size.
Yes
HeightForm height.
Yes
HelpContextIDThe Help ID for the Class Browser.
No
IconThe icon file for the form when minimized.
Yes
lActiveSpecifies whether the Class Browser has focus. For internal use only.
No
lAddInModeIs .T. when an add-in is running and .F. when it is complete.
No
lAddInTraceIf set to .T., the Class Browser lists each method to which an add-in can be hooked as it is executed. See the section on add-ins (beginning with "Putting It All Together with Add-Ins") for more information.
Yes
lAutoExpandDetermines whether class hierarchies are shown expanded or collapsed by default.
Yes
lBusyStateIf .T., a refresh operation is in progress. For internal use only.
No
ldescriptionsIf .T., class descriptions are displayed in Description Edit boxes.
Yes
lDisplayHierarchyErrorIf .T., Display error message if Class Browser attempts to load class with invalid ParentClass.
No
LDragDropFor internal use only.
No
LEmptyFilterIf .T., Empty Class Browser displays methods with no code.
Yes
LErrorError checking status. If .T., an error occurred and Class Browser displays error box. Set to .F. if you want to check for errors.
Yes
lExpandedFor external use only.
No
lFileModeIf .T., the currently selected class is really a file.
No
lFormAddObjectFor external use only.
No
lHiddenFilterIf .T., hidden members are displayed in the members list. Default is .F..
Yes
lIgnoreErrorsFor internal use only.
No
lInitializedFor internal use only.
No
lModalDialogIf .T., Class list is refreshed when Class Browser regains focus after activating a modal dialog box. Default is .T..
Yes
lNoDefaultDetermines whether default method behavior is executed after the add-ins are registered for that method.
Yes
lOutlineOCXIf .T., classes are displayed in a tree view.
No
lParentClassBrowserIf .T., the Edit ParentClass Method toolbar is displayed. Default is .T..
Yes
ProtectedFilterIf .T., protected members are displayed in the members list. Default is .F..
Yes
lReadOnlyThe status of the read-only attribute for the .VCX file associated with the currently selected class.
No
lRefreshModeFor internal use only.
No
lReleaseFor internal use only.
No
lResizeModeFor internal use only.
No
lSCXModeIf .T., the currently selected class is actually an .SCX file.
No
lvcxscxmodeIf .T., the selected class or file selected is a .VCX or .SCX file or is contained by a .VCX or .SCX file.
No
LeftThe left coordinate of the Class Browser form.
Yes
MinHeightThe smallest height the form can have.
No
MinWidthThe narrowest width the form can have.
No
NameThe name of the Class Browser instance.
No
nAtPosFor internal use only.
No
nClassCountThe number of classes in the list.
No
cClassTimeStampReturns timestamp field value of selected class in the class list.
Yes
nClassListIndexThe position of the selected class in the class list.
No
nDisplayModeDisplay mode (hierarchical or alphabetical). 1 is hierarchical and 2 is alphabetical. The default value is 1.
No
 Note that changing this property will not change the display mode. In order to do that, you need to change the value of opgDisplayMode.
 
nFileCountThe number of files open.
No
nInstancesThe number of instances of the currently selected class.
No
nLastHeightThe height of the Class Browser form before it was last changed.
No
nLastRecnoFor internal use only.
No
nLastWidthThe width of the form before the last resize.
No
nPixelOffsetFor internal use only.
No
nRecCountThe total number of records in the .VCX file.
No
nStrLenFor internal use only.
No
oSourceThe reference for an object dropped on a form from the Class Browser.
No
ScaleModeSpecifies how numbers are translated into coordinates.
No
ShowTipsSpecifies whether tooltips for the objects appear or not.
Yes
tcDefaultClassThe name of the class sent through as the second parameter to the Class Browser.
No
tcFileNameThe name of the file to open, passed through on the command line calling the Class Browser.
No
tlListBoxIf .T., the Class Browser displays classes and members in a list box instead of a tree view.
Yes
TopThe top coordinate of the form.
Yes
WidthThe width of the form.
Yes

Working with the properties of the Class Browser provides a neat beginning to customizing it for your own use and tastes. For example, you could change the height of the form with this:

_obrowser.height = _obrowser.height + 10

If you try it, you'll also notice that the form controls will be resized automatically. This happens because changing the height property automatically fires the Resize() method.

Putting It All Together with Add-Ins

Now you know all about the Class Browser internals. You know what the objects, properties, and methods are. What can you do with this knowledge?

The Class Browser has a wonderful little feature called add-ins. Basically, an add-in is a program you write that you can register with the Class Browser. Once registered, you can call the add-in to perform just about anything you like.

An Add-In Program

I will start with a simple example of an add-in. This add-in just displays a wait window with the name of the currently selected class. Although it's not very useful, it does give a good and simple starting place for understanding add-ins. The code is presented in Listing 16.3.


Listing 16.3  16CODE03-Code for a Simple Add-in That Displays the Current Class Name
*  Program...........: CLSNAME.PRG
*  Author............: Menachem Bazian, CPA
*) Description.......: Simple add in for the Class Browser that
*)                   : displays the current class' name.
*  Calling Samples...:
*  Parameter List....:
*  Major change list.:

LPARAMETERS toSource

WAIT WINDOW toSource.ProperBaseClass(toSource.cClass)
RETURN

Note the parameter. When the Class Browser calls the add-in, it passes itself as a parameter to the add-in. This enables you to do all kinds of neat things. Before we get into those, let's look at the nuts and bolts of registering and running add-ins.

Registering the Add-In

Next, register the add-in with the Class Browser. Note that the Class Browser has to be running in order to do this.

_oBrowser.AddIn("Class Name", "ClsName")

The Addin method registers an add-in. The syntax shown here specifies a name for the add-in (Class Name) and the program to run (ClsName.Prg).

Running the Add-In

You can run the add-in in several ways. First of all, the add-in can be run with the DoAddIn method. Here's the syntax for this method:

_oBrowser.DoAddIn("Class Name")

Passing the name of a registered add-in to the DoAddIn method will run that add-in. Another way that it can be called is to right-click on the toolbar and select the Add-In item from the shortcut menu. Selecting the Add-In menu item activates a menu with the installed add-ins. Figure 16.21 shows the Class Browser with the Add-In menu activated from the Add-In button.

Figure 16.21: The Add-In button.

You can manually expand the same menu using the AddInMenu method as follows:

_oBrowser.AddInMenu

Running this method will expand the menu at the location of the mouse pointer. You can close the menu with _oBrowser.DeactivateMenu, although you shouldn't need to because the menu is automatically closed when a selection is made.

Unregistering an Add-In

You can remove an add-in from the Class Browser registration table with the AddIn method too. Here's how to do it:

_oBrowser.AddIn("Class Name",.NULL.)

If the program name is provided as a value of .NULL., the add-in record in the Class Browser registration table is marked for deletion.

TIP
The help file says that the add-in is deleted if the second parameter (the name of the add-in program) is "empty." Don't you believe it. You have to pass through the parameter as a value of .NULL. for the add-in to be deleted.

Boosting Power with Add-Ins

Add-ins let you do all kinds of great things. The example you saw in the last section is a simple one. Basically, it displays the name of the currently selected class by accepting the object parameter (remember, the Class Browser is the parameter) and then accessing the cClass property in the WAIT WINDOW command.

Although this example is not particularly useful, it does illustrate a few key concepts. First, because the Class Browser sends itself as the parameter to the add-ins, every add-in has access to the full power of the Class Browser. That's why it is so important to take the time to learn about the properties and methods of the Class Browser. Another key point is that there is not much that an add-in cannot do for you. The remainder of this chapter talks about examples of this.

Here's another example of an add-in. You saw previously that all the class code shown in this chapter is exported with the Class Browser. For example, if you wanted to add some additional information at the top of the exported code (author name, copyright, and so on), you could do it with the add-in presented in Listing 16.4.


Listing 16.4  16CODE04-Code for an Add-in That Exports Code for a Class with Custom Headings
*  Program...........: DOCCLASS.PRG
*  Author............: Menachem Bazian, CPA
*) Description.......: DocClass is an addin program for
*)                   : the Class Browser that exports
*)                   : the code for a class with a
*)                   : FlashStandard type heading at the top.
*  Calling Samples...:
*  Parameter List....:
*  Major change list.:

LPARAMETERS toObject
#DEFINE cr_lf chr(13)+chr(10)

_cliptext = toObject.ExportClass()

LOCAL laHdrLine[5], lcText, lnCounter

laHdrLine[1] = "*  Class.............: " + ;
        toObject.ProperBaseClass(toObject.cClass)
laHdrLine[2] = "*  Author............: " + ;
        "<Your name>"
laHdrLine[3] = "*  Project...........: " + ;
        "<your project >nameSpecial Edition Using Visual FoxPro 6.0"
laHdrLine[4] = "*  Copyright.........: " + ;
        "(c)<your company name> "
laHdrLine[5] = "*  Notes.............: " + ;
        "Exported code from Class Browser."

lcText = ""

FOR lnCounter = 1 TO ALEN(laHdrLine,1)
    lcText = lcText + ;
                laHdrLine[lnCounter] + cr_lf
ENDFOR

lcText = lcText + cr_lf

_cliptext = lcText + _cliptext
=MessageBox("Code exported to clipboard!", 32)
RETURN

In theory, this add-in is very simple. The ExportClass method is used to dump the code for the class into the Clipboard (you modify the Clipboard by modifying _cliptext). The array holds the additional header lines, which are then added with carriage returns and linefeeds to the output text. Once the add-in is run, all you have to do is paste the code into the word processor. This is a fairly good example of an add-in-it automates a procedure you need to do and reduces it to a few simple keystrokes.

Here's another add-in. This one attempts to provide a runtime preview of an object shown in the Class Browser by bringing up the ObjectInspectorForm shown in Chapter 15. The idea is fairly simple. You make sure that the class library for the class is in the SET CLASS list, instantiate it, and then send it off to the ObjectInspectorForm class (same as in Chapter 15). The code for the program (OBJINSP.PRG) is provided in Listing 16.5.


Listing 16.5  16CODE05-Code for Add-in to Inspect the Current Selected Class
*  Program...........: OBJINSP.PRG
*  Author............: Menachem Bazian, CPA
*  Description.......: Add-in to Inspect the current selected class
Procedure OBJINSP
LPARAMETERS toSource
*-- This Add-in instantiates an object based on
*-- the currently selected class and then passes
*-- it off to the ObjectInspectorForm
*-- class for display.

*-- First check to make sure we are not on the
*-- .VCX

IF '.' $ toSource.cClass
    =MessageBox("You must have a class currently selected!", 16)
    RETURN
ENDIF

*-- Step one, add the Class Library with the object
*-- we are instantiating to the SET CLASS list

LOCAL lcCLassLib
lcClassLibrary = toSource.cFileName
lcCurrentClassLibraries = SET("classlib")

IF !(UPPER(toSource.TrimPath(lcClassLibrary)) # SET("CLASSLIB"))
    SET CLASS TO (lcClassLibrary) ADDITIVE
ENDIF


*-- Instantiate the object
LOCAL loX, loObjInspect
loX = CREATEOBJECT(toSource.cClass)

*-- Send it off to the ObjectInspectorForm
loObjInspect = CREA("ObjectInspectorForm", loX)

*-- Show it as a modal form
loObjInspect.Show(1)

*-- Reset everything
SET CLASSLIB TO (lcCurrentClassLibraries)

*-- We're done
RETURN

* Description: Test Program that creates ObjectInspectorForm objects
*              object to display the Form showing the__Screen object
*              properties.  Example:
*   oObjInspect = CREATE("ObjectInspectorForm",_Screen)
*   oObjInspect.Show()
*  Class.............: Objectinspectorform
*  Author............: Menachem Bazian, CPA
*  Project...........: Special Edition Using Visual FoxPro 6
*  Copyright.........: (c) Flash Creative Management, Inc. 19951998
*) Description.......:
*  Notes.............: Exported code from Class Browser.

**************************************************
*-- Class:        objectinspectorform (d:\data\docs\books\vfu\code\oop2.vcx)
*-- ParentClass:  form
*-- BaseClass:    form
*
DEFINE CLASS objectinspectorform AS form

    DataSession = 2
    Height = 309
    Width = 466
    DoCreate = .T.
    AutoCenter = .T.
    BackColor = RGB(192,192,192)
    BorderStyle = 3
    Caption = ""
    MaxButton = .F.
    MinButton = .F.
    ClipControls = .F.
    WindowType = 1
    PROTECTED cobjectbmpfile
    cobjectbmpfile = (home()+"SAMPLES\GRAPHICS\BMPS\FOX\APPS.BMP")
    PROTECTED cmethodbmpfile
    cmethodbmpfile = (home()+"SAMPLES\GRAPHICS\BMPS\FOX\CLASSES.BMP")
    PROTECTED cpropertybmpfile
    cpropertybmpfile = (home()+"SAMPLES\GRAPHICS\BMPS\FOX\INDEXES.BMP")
    PROTECTED ceventbmpfile
    ceventbmpfile = (home()+"SAMPLES\GRAPHICS\BMPS\FOX\AUTOFORM.BMP")
    Name = "objectinspectorform"

    ADD OBJECT lstmembers AS listbox WITH ;
        FontName = "Courier New", ;
        Height = 205, ;
        Left = 12, ;
        Top = 48, ;
        Width = 438, ;
        ItemBackColor = RGB(192,192,192), ;
        Name = "lstMembers"

    ADD OBJECT cmdok AS commandbutton WITH ;
        Top = 264, ;
        Left = 180, ;
        Height = 37, ;
        Width = 109, ;
        Cancel = .T., ;
        Caption = "OK", ;
        Default = .T., ;
        Name = "cmdOK"

    ADD OBJECT label1 AS label WITH ;
        AutoSize = .F., ;
        BackStyle = 0, ;
        Caption = "Order:", ;
        Height = 18, ;
        Left = 24, ;
        Top = 15, ;
        Width = 40, ;
        Name = "Label1"

    ADD OBJECT opgorder AS optiongroup WITH ;
        ButtonCount = 2, ;
        BackStyle = 0, ;
        Value = 1, ;
        Height = 25, ;
        Left = 96, ;
        Top = 12, ;
        Width = 217, ;
        Name = "opgOrder", ;
        Option1.BackStyle = 0, ;
        Option1.Caption = "Alphabetical", ;
        Option1.Value = 1, ;
        Option1.Height = 18, ;
        Option1.Left = 0, ;
        Option1.Top = 4, ;
        Option1.Width = 109, ;
        Option1.Name = "optAlphabetical", ;
        Option2.BackStyle = 0, ;
        Option2.Caption = "Grouped", ;
        Option2.Value = 0, ;
        Option2.Height = 18, ;
        Option2.Left = 127, ;
        Option2.Top = 4, ;
        Option2.Width = 73, ;
        Option2.Name = "optGrouped"

    PROCEDURE buildlist
        LPARAMETERS toObject

        #DEFINE COLUMNLENGTH 25

        WAIT WINDOW NOWAIT "Building members list. Please stand by..."
        this.lockscreen = .t.
        this.lstMembers.clear()
        SELECT _members
        IF this.opgOrder.value = 1
            SET ORDER TO TAG Alpha
        ELSE
            SET ORDER TO TAG Grouped
        ENDIF

        GO TOP
        lnCounter = 0
        SCAN
            lnCounter = lnCounter + 1
            lcText = PADR(_members.cMembName, COLUMNLENGTH)

            IF _members.cMembType = "Prop"
                *-- Now we need to get the value of the property
                lcText = lcText + ALLTRIM(_members.cMembVal)
            ENDIF

            thisform.lstMembers.additem(" " + lcText)
            lcBmpVar = "this.c" + alltrim(_members.cMembType)+"bmpfile"
            thisform.lstMembers.picture(lnCounter) = EVAL(lcBmpVar)
        ENDSCAN

        this.lockscreen = .f.
        thisform.refresh()
        WAIT CLEAR
    ENDPROC

    PROCEDURE Resize
        this.lockscreen = .t.

        this.lstMembers.width = this.width - 24
        this.cmdOk.left = (this.width-this.cmdOk.width)/2
        this.cmdOK.top = (this.height - 8 - this.cmdOK.height)
        this.lstMembers.height = this.cmdOK.top - this.lstMembers.top - 11

        this.lockscreen = .F.
    ENDPROC

    PROCEDURE Init
        *  Class.............: OBJECTINSPECTORFORM
        *  Author............: Menachem Bazian, CPA
        *  Project...........: Visual FoxPro Unleashed
        *  Created...........: May 16, 19951998 - 07:44:03
        *  Copyright.........: (c) Flash Creative Management, Inc., 19951998
        *) Description.......: When passed an object, it builds a list of the
        *)                   : members and displays them nicely.
        *  Calling Samples...: oForm = CREATEOBJECT("OBJECTINSPECTORFORM", 
              oObject)
        *  Parameter List....: toObject - Object to inspect
        *  Major change list.:

        LPARAMETERS toObject

        this.caption = "Inspecting object: " + ALLT(toObject.Name)

        IF TYPE("toObject") # 'O'
            =MessagebOx("You can only pass OBJECT type parameters!", 16)
            RETURN .F.
        ENDIF

        *-- If we get this far, we can inspect the object
        *-- Let's define some memory variables and do the AMembers()

        LOCAL laMembers[1], lcText, lcName
        =AMembers(laMembers,toObject,1)

        *-- In order to create the list in proper order, it is useful to have
        *-- a table to work off of (so we can INDEX). Hence:

        CREATE CURSOR _members (cMembName C(25), cMembtype C(25), cMembVal 
           C(40))
        INSERT INTO _members FROM ARRAY laMembers

        INDEX ON cMembName TAG Alpha
        INDEX ON cMembType + cMembName TAG Grouped

        SCAN FOR _members.cMembType = "Prop"

            *-- Determine the value of the property and place it in the
            *-- cMembVal field

            lcName = "toObject."+ALLTRIM(_members.cMembName)
            DO CASE
                CASE TYPE(lcName) = 'U'
                    lcText = ".UNDEFINED."
                CASE isnull(EVAL(lcName))
                    lcText = ".NULL."
                CASE TYPE(lcName) = 'L'
                    lcText = IIF(EVAL(lcName), ".T.", ".F.")
                OTHERWISE
                    lcText = ALLTRIM(PADR(EVALUATE(lcName),50))
            ENDCASE
            REPLACE _members.cMembVal WITH lcText
        ENDSCAN

        this.buildlist()
    ENDPROC

    PROCEDURE cmdok.Click
        release thisform
    ENDPROC

    PROCEDURE opgorder.Click
        thisform.buildlist()
    ENDPROC

ENDDEFINE
*
*-- EndDefine: objectinspectorform

Again, note how the Class Browser as a parameter is so useful. The Class Browser's properties give the necessary information to check that the current class is a class and not the .VCX file itself, what the class library needed is, and so on.

Changing Class Browser Behavior with Add-Ins

By installing the sample add-in in the manner shown in the previous section, you can run it by selecting a class, clicking the Add-In command button, and selecting the appropriate menu item.

Go back to the ObjInsp add-in for a moment. This is a powerful add-in. However, when you think about it, it is a utility to see what a particular class is like. This seems to be analogous to the Export command button, which is designed to show the class code for a "quick view" of the class.

If a left-mouse-click on the View Class Code command button exports the code to a window, wouldn't it be nice to have a right-mouse-click on the command button run the ObjInsp add-in? In fact, you can do just that. To show how this is accomplished, take a closer look at the syntax of the AddIn() method:

AddIn(<AddIn Name>, ;<Program Name>, ;<Method Name>, ;<Active For Files>, ;
      <PlatForm>, ;<Comment>)

This method installs or removes a Class Browser add-in.

The <AddIn Name> parameter is the name of the add-in. The name of the add-in can be one or more words, and case is not important (except that it will be displayed on the menu in the same manner as it is specified here). The add-in will be displayed in the Add-In menu if the third parameter (Method Name) is not specified.

The <Program Name> parameter is the name of a program to run. The program can be a .PRG, .APP, .EXE, or .FXP. In addition, you can specify an .SCX file. The assumed extension is .PRG; you can specify other extensions as appropriate.

The <Program Name> parameter can also be the name of an object. The syntax for this is <VCX FileName, Class Name>. Here is an example:

_oBrowser.AddIn("MyAddIn", "MyVcxVCX, MyClass")

When the add-in is run, the class MyClass of MyVcx.VCX file is instantiated. Remember that an object parameter is sent through to the class.

It's not a bad idea to specify a full path to the program in this parameter. This ensures that the add-in runs properly wherever you are in your hard disk. If the Class Browser cannot find the program or .VCX file when it tries to call an add-in, an error is generated.

The <Method Name> parameter is the name of a method to automatically call the add-in. Any valid method, either a method of the Class Browser form or one of its objects, can be a hook for a method. For example, if you wanted to hook MyAddIn to the Export command button's Click method, you could state this:

_oBrowser.AddIn("MyAddIn", , "CmdExport.Click")

Notice, by the way, that I did not specify the second parameter in this command. Because I did not need to modify the parameter, I left it out. The AddIn() method is smart enough to handle that.

This parameter is the key to the task that was just set out. By specifying the cmdExport.RightClick method in the third parameter, you can hook the add-in to the RightClick() event on the Export command button. The call to the Class Browser AddIn() method consists of the following line of code:

_obrowser.Addin("View Object Members",
         "ObjInsp.prg","cmdExport.RightClick")

Once the add-in is installed, you can select a class, right-click on the View Class button, and the Inspecting Object dialog appears, as shown in Figure 16.22.

Figure 16.22: Right-click on the View Class Code button and the Inspecting Object dialog box appears.

By the way, if you hook into a method that already has behavior (the Click method of cmdExport calls ExportClass, for example), the add-in runs first and then the original behavior runs. If you have multiple add-ins registered for a method, they will run in the order they were registered.

Finally, if you want to totally override the behavior of a method (you do not want cmdExport to call ExportClass, for example), the add-in must set the lNoDefault property on the Class Browser to .T.. If this property is set to .T., the Class Browser method will detect this and ignore the rest of the method.

The <Active For Files> parameter enables you to determine the files for which the add-in will work. By default, when an add-in is added to the Class Browser registration table, it is available for all the .VCX and .SCX files with which you might work. This parameter enables you to specify a file or list of files (separated by commas) for which this add-in is available. For other files loaded into the Class Browser, the add-in simply does not exist.

The <Platform> parameter specifies the platform (for example, Windows) in which the add-in will work. By default, it is available for all platforms.

The <Comment> parameter is an optional comment you can store with the add-in.

If you take a close look at the AddIn method, it gives a wealth of insight into the power and flexibility of the Class Browser. With add-ins, you can totally customize the Class Browser to run your code at almost any interval.

For example, assume you prefer to use Courier New as the font for the Class Browser. All you need to do is create an add-in like the one presented in Listing 16.6 (FONTSET.PRG) and register it for the Init method.


Listing 16.6  16CODE06-Code for an Add-in That Changes the Class Browser Font
*  Program...........: FONTSET.PRG
*  Author............: Menachem Bazian, CPA
*) Description.......:
*  Calling Samples...:
*  Parameter List....:
*  Major change list.:

LPARAMETERS toSource

toSource.SetFont("Courier New")

Here is how to register the add-in:

_oBrowser.AddIn("FontSet", "FontSet", "Init")

© Copyright, Sams Publishing. All rights reserved.