04.08.2013 Views

Printing - FoxTalk - dFPUG-Portal

Printing - FoxTalk - dFPUG-Portal

Printing - FoxTalk - dFPUG-Portal

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

January 1996<br />

Extend the Visual FoxPro Class Browser<br />

with Add-Ins<br />

Ken Levy<br />

The Class Browser provided with the Professional Edition of Visual FoxPro takes the term "open<br />

architecture" to new heights. If you don't like the Class Browser, you'll learn how to customize it to<br />

your heart's content.<br />

Working with and managing class libraries isn't easy. Visual FoxPro's Class Browser provides an<br />

easy, user-friendly interface for dealing with Visual Class Libraries (if you're not familiar with<br />

the Class Browser, see the sidebar, "Class Browser Basics"). However, ease of use represents<br />

only a fraction of its power. The Class Browser also supports the unique ability to be totally<br />

customized using what are known as add-ins.<br />

Flexibility and the Class Browser<br />

The key to a tool is how well it can work in getting a job done. Managing class libraries carries<br />

with it a myriad of chores, from checking the code in a class to merging it with the production<br />

libraries.<br />

In many development environments, the Class Librarian molds the way they do their jobs into<br />

the way the tool they use allows them to perform their tasks. Visual FoxPro, in true keeping with<br />

the history of both Microsoft's Visual development tools and the FoxPro product line, not only<br />

provides a simple to use tool for managing class libraries but has also made the architecture of<br />

this utility totally open so that it can be customized to fit the needs of every individual. This<br />

customization is accomplished using add-ins.<br />

An add-in is a program that you write and "register" with the Class Browser. Once registered,<br />

this program can execute automatically in response to an event in the Class Browser or can be<br />

called manually by the user. An add-in can do just about anything you like. Here's an example of<br />

an add-in program:


* Program...........: CLSNAME.PRG<br />

*) Description.......: Simple add-in for the Class<br />

*) : Browser that<br />

*) : displays the current class' name.<br />

* Calling Samples...:<br />

* Parameter List....:<br />

* Major change list.:<br />

LPARAMETERS toSource<br />

WAIT WINDOW toSource.ProperBaseClass(toSource.cClass)<br />

RETURN<br />

Note the parameter. The Class Browser, when calling the add-in, passes itself as a parameter to<br />

the add-in, meaning all the member objects, properties, events, and methods are available for<br />

reference. In the example just shown, the Class Browser's ProperBaseClass() method and cClass<br />

property are used in the add-in.<br />

This capability allows you to do many things. You can use the information in the Class Browser<br />

to guide what you want to do (for example, if the currently selected class is really the .VCX file,<br />

you could loop through all the classes in the .VCX as opposed to just the class currently<br />

selected), access the methods or even get at the .VCX table itself. Understanding and using this<br />

powerful feature is key to creating add-ins.<br />

First let's cover the nuts and bolts of registering and running add-ins.<br />

Registering add-ins<br />

Before you can use an add-in, it has to be registered. The Class Browser has to be running in<br />

order to do this:<br />

_oBrowser.AddIn("Class Name", "ClsName")<br />

The AddIn() method registers an add-in. The syntax shown here specifies a name for the add-in<br />

(Class Name) and the program to run (ClsName.Prg). The name is case-insensitive, but the way<br />

it's specified here is the way it will show in the add-in menu.<br />

Running the add-in<br />

The add-in can be run in several ways. First of all, you can run it with the DoAddIn() method:<br />

_oBrowser.DoAddIn("Class Name")


Passing the name of a registered add-in to the DoAddIn() method will run that add-in.<br />

You can also call the add-in with the add-in button. Clicking on the add-in button activates a<br />

menu with the installed add-ins. The same menu can be expanded manually with the<br />

AddInMenu() method as follows:<br />

_oBrowser.AddInMenu()<br />

Running this method will expand the menu at the location of the mouse pointer. You can close<br />

the menu with _oBrowser.DeactivateMenu(), although you shouldn't need to because the menu is<br />

automatically closed when a selection is made.<br />

Unregistering an add-in<br />

You can also remove an add-in from the Browser Registration table with the AddIn() method:<br />

_oBrowser.AddIn("Class Name",.NULL.)<br />

If the program name is provided as a value of .NULL., the add-in record in the browser<br />

registration table is marked for deletion.<br />

Creating custom add-ins<br />

Add-ins let you control many events and extend or override existing behavior. The previous<br />

example is a trivial one that simply displays the name of the currently selected class. It<br />

accomplishes this by accepting the object parameter (remember, the Class Browser form object<br />

is the parameter) and then accessing the Class Browser's cClass property in the WAIT<br />

WINDOW command.<br />

While this example isn't particularly useful, it does illustrate a few key concepts. First, by<br />

sending itself as the parameter to the add-ins, every add-in has access to the full power of the<br />

Class Browser. That's why it's so important to take the time to learn about the objects, properties,<br />

events, and methods of the Class Browser (they're all documented in the Visual FoxPro help<br />

file). Another key point here is that there isn't much that an add-in can't do for you.<br />

Here's another example of an add-in: This one inserts additional header information to the code<br />

exported with the Class Browser's ExportClass() method. It's useful when you have to represent<br />

a class in code. You may need to do this for system documentation, when writing articles or<br />

books, and so forth:


* Program...........: DOCCLASS.PRG<br />

*) Description.......: DocClass is an add-in program for<br />

*) : the Class Browser that exports<br />

*) : the code for a class with a<br />

*) : standard heading at the top.<br />

* Calling Samples...:<br />

* Parameter List....:<br />

* Major change list.:<br />

LPARAMETERS toObject<br />

#DEFINE cr_lf chr(13)+chr(10)<br />

_cliptext = toObject.ExportClass()<br />

LOCAL laHdrLine[5], lcText, lnCounter<br />

laHdrLine[1] = "* Class.............: " + ;<br />

toObject.ProperBaseClass(toObject.cClass)<br />

laHdrLine[2] = "* Author............: " + ;<br />

"Joe Developer"<br />

laHdrLine[3] = "* Project...........: " + ;<br />

"My Project"<br />

laHdrLine[4] = "* Copyright.........: " + ;<br />

"(c) My Company, Inc. 1995"<br />

aHdrLine[5] = "* Notes.............: " + ;<br />

"Exported code from Class Browser."<br />

lcText = ""<br />

FOR lnCounter = 1 TO ALEN(laHdrLine,1)<br />

lcText = lcText + laHdrLine[lnCounter] + cr_lf<br />

ENDFOR<br />

lcText = lcText + cr_lf<br />

_cliptext = lcText + _cliptext<br />

=MessageBox("Code exported to clipboard!", 32)<br />

RETURN<br />

In theory, this add-in is very simple. The ExportClass() method is used to dump the code for the<br />

class into the Clipboard (modifying _CLIPTEXT modifies the Clipboard). The array holds the<br />

additional header lines, which are then added -- with carriage returns and linefeeds -- to the<br />

output text. Then you can paste the outputted text wherever it's needed.<br />

This is a fairly good example of an add-in in that it automates a procedure and reduces it to two<br />

simple mouse clicks.<br />

Changing Class Browser behavior with add-ins<br />

By installing add-ins in the manner shown earlier, you can run it by selecting a class, clicking on<br />

the add-in command button, and selecting the appropriate menu item.


Sometimes an add-in most appropriately enhances or replaces existing functionality in the Class<br />

Browser. For example, the DocumentClass add-in can be viewed as analogous to the View Class<br />

command button. You could say it would be appropriate to hook this add-in not to the add-in<br />

menu but rather to the RightClick event of the View Class button.<br />

To show how this is done, let's take a closer look at the syntax of the AddIn() method:<br />

AddIn(, ;<br />

, ;<br />

, ;<br />

, ;<br />

, ;<br />

)<br />

is the name of the add-in. The name can be one or more words and case isn't<br />

important except that it will display on the menu in the same manner as you specify here. The<br />

add-in will display in the add-in menu if the third parameter (Method Name) isn't specified.<br />

is the name of a program to run. The program can be a .PRG, .APP, .EXE, or<br />

.FXP. In addition, you can specify an .SCX. The assumed extension is .PRG; specify other<br />

extensions as appropriate.<br />

can also be the name of an object. The syntax for this would be , for example:<br />

_oBrowser.AddIn("MyAddIn", "MyVcx.VCX, MyClass")<br />

When the add-in is run, the class MyClass of MyVcx.VCX is instantiated. Remember that an<br />

object parameter will be sent through to the class.<br />

It's a good idea to specify a full path to the program in this parameter, ensuring that the add-in<br />

runs properly wherever you are in your hard disk. If the Class Browser can't find the program or<br />

.VCX when trying to call an add-in, an error will be generated.<br />

is the name of a method to automatically call the add-in. Any valid method,<br />

either a method of the Class Browser form or one of its objects, can be a hook for a method. For<br />

example, if you want to hook MyAddIn to the Export CommandButton's RightClick event, you<br />

can use the following code:<br />

_oBrowser.AddIn("MyAddIn", , "CmdExport.Click")<br />

By the way, you don't have to specify the second parameter in this command. This allows you to<br />

change the registration of an add-in only as much as you need. In this case, since the program<br />

that will run when MyAddIn is run doesn't change, there's no need to specify that parameter.


Leaving it blank will pass a logical .F. to the corresponding parameter variable in the Class<br />

Browser's AddIn method. The AddIn() method is intelligent enough to understand that an empty<br />

parameter means that the corresponding value remains unchanged.<br />

The third parameter is the key to this task. By specifying the cmdExport.RightClick method in<br />

the third parameter, the add-in is hooked to that event and will be called by that event when it<br />

fires.<br />

If you hook into a method that already has behavior (for example, the click method of<br />

cmdExport calls ExportClass()), the add-in will run first, followed by the original behavior. If<br />

you have multiple add-ins registered for a method, they will run in the order they were<br />

registered.<br />

Finally, if you want to totally override the behavior of a method (for example, you don't want<br />

cmdExport to call ExportClass()), the add-in must set the lNoDefault property of the Class<br />

Browser to .T. If this property is set to .T., the Class Browser method will detect this and won't<br />

execute the rest of its code. It's not necessary for the add-in to reset lNoDefault to .F.; the Class<br />

Browser does this automatically just before it calls any add-in.<br />

allows you to determine which files the add-in will work for. By default,<br />

when an add-in is added to the Class Browser registration table, it's available for all .VCX and<br />

.SCX files you may work with. This parameter allows you to specify a file or list of files<br />

(separated by commas) that this add-in will be available for. For other files loaded into the Class<br />

Browser, the add-in will in effect simply not exist.<br />

specifies the platform (for example, "Windows") the add-in will work in. By default,<br />

it is available for all platforms.<br />

is an optional comment you can store with the add-in.<br />

If you take a close look at the AddIn() method, it provides a wealth of insight into the power of<br />

the Class Browser and its flexibility. Add-ins allow you to customize the Class Browser to run<br />

your code at almost any interval. For example, assume you prefer to use Courier New as the font<br />

for the Class Browser. All you need to do is create an add-in like this and register it for the Init()<br />

method:<br />

* Program...........: FONTSET.PRG<br />

* Author............: Menachem Bazian, CPA<br />

* Copyright.........: (c) Flash Creative Management, Inc., 1995<br />

*) Description.......: Set the fonts for the Class<br />

LPARAMETERS toSource<br />

toSource.SetFont("Courier New")<br />

RETURN<br />

To register it:<br />

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


Summary<br />

As you can see, the Class Browser is quite malleable. Most aspects of its appearance and<br />

behavior can be modified with just a little bit of coding to suit your particular needs and tastes. I<br />

hope that this article has inspired you to explore the Class Browser and use it more effectively in<br />

your work.<br />

Ken Levy is a software engineer at Flash Creative Management Inc., a consulting and software<br />

development firm in River Edge, New Jersey. Ken spends most of his time working in the Los Angeles<br />

area, specializing in the research, design, and creation of application development tools. While working<br />

as a full-time consultant at NASA's Jet Propulsion Laboratory in Pasadena, California, Ken developed<br />

several public domain programs including GENSCRNX, PTX Editor, and other FoxPro tools.<br />

201-489-2500, CompuServe 76350,2610.<br />

Sidebar: Class Browser Basics<br />

The Class Browser in FoxPro 3.0 (see Figure 1) is written in FoxPro and is specified by a system<br />

variable named _BROWSER, which defaults to _BROWSER=SYS(2004)+'BROWSER.APP'.<br />

The Class Browser opens a single visual class library (VCX). The Class Browser is included<br />

with the Professional Edition of Visual FoxPro and allows you to see the structure of a visual<br />

class or form at a glance, view equivalent code, change the class hierarchy, and so on.<br />

The Class Browser user interface<br />

The Class Browser is launched by selecting Class Browser from the Visual FoxPro Tools menu.<br />

This causes Visual FoxPro to execute the program defined in a system variable called<br />

_BROWSER. The Class Browser supports both visual and non-visual (Custom) classes. Multiple<br />

instances of the Class Browser can be launched. Selecting Class Browser from the Tools menu<br />

always launches a new instance; the window list on the Window menu is used to activate an<br />

existing instance.<br />

If the Class Browser form is resized, the controls will be resized and moved to fit the available<br />

space. If the Class Browser form height is changed, the class display outliner and the member<br />

form pages height will change. If the Class Browser form width is changed, the class display<br />

outliner, member form pages, class description, member/instance description, and class type<br />

combo box height will change. Classes can be moved from one .VCX to another using<br />

drag-and-drop.<br />

Using Class Browser with Class Designer and Form Designer<br />

Components of an application are developed in the Class Designer (which saves the class in a<br />

VCX) by creating classes visually, while applications are assembled from those components in<br />

the Form Designer (which saves forms in an SCX). Not every line of method code and every<br />

property setting needs to be saved as a class when building an application. Adding code and<br />

setting properties of objects in an SCX makes sense if you don't intend to reuse specialized<br />

changes. The SaveAsClass function makes it easy to create classes in a .VCX from objects in an


SCX, should you need to subclass the functionality of a form.<br />

Since an individual class library can contain any number of classes, and the Class Designer can<br />

edit only one class at a time, the menu and command line interfaces to access the Class Designer<br />

are clumsy. It's much easier to navigate a class hierarchy in the Class Browser and invoke the<br />

Class Designer for the class you've selected in the Browser.<br />

The Class Designer allows developers to create classes visually. These classes can be either<br />

visual or non-visual (Custom) subclasses. Custom properties and methods can be added to a<br />

class and will appear in the Properties window for visual editing. The Form Designer allows<br />

forms to be created by subclassing existing classes by dropping them onto the form surface from<br />

the Controls toolbar.<br />

The object model in Visual FoxPro supports container classes. Container classes are classes that<br />

can contain objects as members. For example, Forms and FormSets in an SCX are containers.<br />

The FormSet can contain Form objects, while the Form can contain control and Custom objects,<br />

as well as other containers such as OptionGroups and CommandGroups. The Toolbar class is a<br />

container that can contain control objects. Control classes such as the CommandButton class<br />

aren't container classes since they can't contain objects.<br />

If you experiment with the Save As Class dialog box in the Form Designer, you'll notice that you<br />

can save controls as classes into a .VCX . You can also select multiple controls and save all<br />

selected controls. You can even save an entire form (including its object members) as a single<br />

class. When you select an individual control (or just the form) and you save the selected (single)<br />

object as a class, you are creating a class based on the object's classification. When you save a<br />

group of selected controls or the entire form that contains controls into a single class, you are<br />

actually saving a container class.<br />

A Wild Ride, But Worth It<br />

Bob Grommes<br />

I've been editor of <strong>FoxTalk</strong> now for two and a half years. During my watch, <strong>FoxTalk</strong> has grown<br />

from 24 to 32 pages, we've redesigned the visuals, added many new features, and covered the<br />

release of FoxPro 2.6 and Visual FoxPro. Two and a half years ago we still struggled with 286<br />

client machines; now we fret about those slow 386 machines and how to shoehorn applications<br />

into 8M of RAM. Less than a year ago, we passed arguments to functions, and now we send<br />

messages to methods. As the saying goes, "the only constant in this business is change."<br />

But what really defines the essence of an experience are the people -- subscribers and the staff at<br />

Pinnacle alike. My experience as editor has been a richly rewarding time of service to the<br />

FoxPro community. I've made a lot of friends, had a lot of fun, and grown tremendously.<br />

So it's with many warm memories like these, and with more than a little regret, that I've decided<br />

to leave my post as editor to concentrate on other projects, including a Visual FoxPro book<br />

planned for late 1996. It was a difficult decision. Thank you for your letters, suggestions, and


constructive criticisms. I treasure them all. The loyalty of <strong>FoxTalk</strong> subscribers is something to<br />

behold. You really care about this publication!<br />

I hope you'll be as kind to my successor, Whil Hentzen . Whil needs little introduction: he's been<br />

a regular columnist in <strong>FoxTalk</strong> since before my time, has served the past few months on our<br />

Editorial Advisory Board, and is probably one of the best-connected and best-liked folks in the<br />

Fox community. He's president of Hentzenwerke Inc. in Milwaukee, Wisconsin, a FoxPro<br />

development firm that each November sponsors the Great Lakes Great Database Workshop for<br />

FoxPro developers. In addition, Whil is a driving force behind the FoxPro user group in his area,<br />

as well as the author of the new book Programming Visual FoxPro 3.0 from Ziff-Davis.<br />

Somehow, he finds the time to run competitively and raise three children with his wife, Linda.<br />

I hope that you'll take a moment to extend Whil a warm welcome (he can be reached on<br />

CompuServe 70651,2270).<br />

I won't be disappearing entirely from these pages; indeed, I may have more time to write articles<br />

than I did as editor! I wish you and yours the best of success in the coming years, and invite you<br />

to keep in touch (I can still be reached on CompuServe 74126,72).<br />

Tip: Set Focus to a Control When a Record is<br />

Added<br />

William O'Connor<br />

To set focus to a particular control when a record is added, add a new property to your form<br />

called cAddfield. Then add this method and give it the name of your choice:<br />

* This method requires the form to have a property<br />

* called cAddField. cAddField MUST contain a fully<br />

* specified object path pointing to the object.<br />

* For example:<br />

* cAddField = 'PageFrame1.Page1.txtEmployee'<br />

* Please note that this will set focus, but won't change<br />

* the page to show the field if it is on the non-current<br />

* page of a pageframe.<br />

IF TYPE("THISFORM.cAddField") = "C" AND ;<br />

NOT EMPTY(THISFORM.cAddField)<br />

cObjName = ALLTRIM(ThisForm.cAddField)<br />

= EVALUATE("THISFORM." + ALLTRIM(cObjName) ;<br />

+ ".SetFocus()")<br />

ENDIF


If you named this method SetFocusOnAdd, you'd execute it just after adding a record using a<br />

call like this:<br />

THISFORM.SetFocusOnAdd()<br />

Why not just call the SetFocus method for the specific object directly? Because maintaining and<br />

subclassing the form is easier this way, particularly if you need to perform this operation from<br />

many places within your form, or even from outside the form (say, in an object that manages<br />

database access or in a stored procedure). In that latter situation, you could issue a command like<br />

this:<br />

_SCREEN.ActiveForm.SetFocusOnAdd()<br />

William O'Connor is a senior programmer with ContrAcct Systems Corp. of Naperville, Illinois. E-mail:<br />

bocon@aol.com.<br />

Liven Up Your Applications with 3-D<br />

Graphical/Text Push Buttons<br />

Paul Russell (1)<br />

While Visual FoxPro allows you to combine text and graphics on the same control, it's also possible<br />

to achieve the same effect in FoxPro 2.x for Windows; indeed, this technique may at times still be<br />

necessary in Visual FoxPro to get the effect you want because text is always displayed centered<br />

under the graphic.<br />

Push buttons aren't the most exciting object in a FoxPro application, especially the ones that<br />

shipped with early versions of Windows 3.0 applications. They had the requisite text, and you<br />

could tell that they were a button, but they weren't very interesting. With more powerful<br />

computers, and with developers always striving to be more flashy, this has been changing. We<br />

have gone from the old style push button, like the two images on the left in Figure 1 (the button<br />

in the middle is actually a Windows 95 push button), to a combined image plus text push button,<br />

like the one on the right in .<br />

As you click on the buttons on the left, the text responds by moving down and to the right,<br />

giving the impression that the button is being "pushed in." Then, when the mouse button is<br />

released, the buttons resume their normal appearance, giving the impression that they have


"come back out."<br />

The button on the right behaves a bit differently. When you click on this button, the text and the<br />

shadow below the check mark are moved down and to the right, but the arrow itself stays in its<br />

original position. This gives the impression that this button has depth and is a bit closer to being<br />

3-D.<br />

How do you do this?<br />

You need to combine a number of tricks to get this to work in FoxPro for Windows 2.x (this<br />

obviously won't be relevant for the character-based versions of FoxPro, and it doesn't fit with the<br />

look and feel of the Mac). Essentially, you have to do the following:<br />

• Load multiple images for a single button.<br />

• Generate the images that you want to display.<br />

• Adjust the position of the second image.<br />

• Allow for white space if necessary.<br />

• Load multiple images for a single button.<br />

FoxPro has three states for a push button: Up, Down, and Disabled. Using this information, you<br />

can have one image for the "Up" state of the button, and another for the "Down" state. To<br />

include different bitmaps for the different states, separate them with commas.<br />

Unfortunately, the Screen Builder supports only a single image for each button. This is because<br />

it verifies that the file exists, and then surrounds the filename with code to allow the user to find<br />

it at runtime, just in case it wasn't included within the .EXE file.<br />

This means that you have to use GENSCRNX to override the image definition of the @ GET<br />

command. In the Comment snippet of the push button that you want to change, in this case the<br />

"OK" push button, include this GENSCRNX directive:<br />

*:PICTURE OK_UP.BMP,OK_DOWN.BMP<br />

This will replace the LOCATE() function that GENSCRN puts into the .SPR file with your<br />

definition. You can also construct the list of images in a memory variable and insert the<br />

following GENSCRNX directive:<br />

*:PICTURE m.lcButtons<br />

Generate the Images that you want to Display<br />

Using the "OK" button as an example, you want the green portion of the arrow and the text to<br />

remain in one place and the shadow to move one pixel down and to the right. It's easiest to


generate the "un-pressed" image first, and then reload the image into your bitmap editor and<br />

make the changes manually. (I usually prefer to do this while Zoomed into the image so that I<br />

have a better view of the pixels themselves.)<br />

Figure 2 shows the two images generated by this process. The text in the second button has been<br />

moved up, and that the shadow underneath the arrow has been moved down and to the right.<br />

When these two images are set up in a screen, you'll see that the shadow in the pressed button<br />

moves, but so does the rest of the image. This is because FoxPro moves the whole image down<br />

and to the right by a single pixel. This needs to be countered.<br />

Adjusting the position of the second image<br />

Moving the second image up and to the left by one pixel corrects the look of the button. The<br />

arrow and "OK" text stay in their original position, but the shadow will move down and to the<br />

right by two pixels. Figure 3 shows the two final images.<br />

Allow for white space if necessary<br />

In bitmaps, white represents the background. When FoxPro sees white in the bitmap, it turns it<br />

into whatever color required for the button background, gray for the "un-pressed" button and<br />

light gray for the pressed button. This becomes obvious if you have gray as the background of<br />

the button image (try some of the sample buttons that Microsoft ships with FoxPro for<br />

Windows).<br />

You can display the color white on your buttons, but the method isn't obvious.<br />

If, for example, you want to mimic the appearance of the Windows 3.x Information message<br />

box, you will want to have a white exclamation point inside a blue circle. Typically, this would<br />

be drawn as shown in Figure 4.<br />

As you might have guessed, the problem with this is that the exclamation point will be the same<br />

color as the background behind the button: gray in the un-pressed button and light gray in the<br />

pressed button.<br />

You can override this default appearance by creating a "mask" of the image. A mask is a .BMP<br />

file that has the same filename as the original image, but with .MSK as the extension. Of course,<br />

the easiest way to create this is to define the bitmap that you want first, then copy it to a file with<br />

the .MSK extension (see Figure 5).<br />

The mask file contains only two colors: black and white. Wherever the color is black, the image<br />

itself shows through. Where the mask is white and the image is white, the background of the<br />

button shows through, and where the mask is white and the image is anything other than white, it<br />

seems like a negative of the image shows through. Table 1 shows this a bit more clearly.<br />

Table 1. Mask file vs. the image.<br />

White areas of .BMP Non-white areas of .BMP<br />

White portion of .MSK file Button Background Negative of the .BMP color


Black portion of .MSK file White The .BMP Color<br />

Conclusion<br />

The FoxPro manual shows most of the commands and functions that you can use along with<br />

some straightforward examples. What the manual can't show you is what you can do with this<br />

information. Whenever I see constructive, inventive interface and reporting requirements, I try to<br />

figure out what the author did to get that effect. Frequently, as with both of the examples here,<br />

the answer was staring me in the face in the manual.<br />

RTFM (Remember The "Fantastic" Manual), because it can frequently solve an elusive problem.<br />

The manual might not be the easiest or the best place to look for an answer, but it should be the<br />

first place that you look.<br />

Paul Russell, a software specialist with Software Kinetics Ltd., is chairman of the Fox Users of Nova<br />

Scotia, and is an active member of the FoxPro sections on the Internet. He has been programming for<br />

more than 10 years, with more than seven of those years of experience with the Fox product line. Internet<br />

prussell@fox.nstn.ca, or russell@atl.sofkin.ca, CompuServe 102440,2125.<br />

Subclassing Visual FoxPro Base Classes<br />

Richard A. Schummer (2)<br />

To subclass or not to subclass, that's the question. When, if ever, should you use Visual FoxPro base<br />

classes directly in your applications?<br />

At DevCon 95 and during the Visual FoxPro beta, the conventional wisdom was that developers<br />

should subclass all of Visual FoxPro's base classes. I went with the crowd at first, but then I<br />

changed course and used the Visual FoxPro-provided base classes directly. This article is about a<br />

bad decision and a learning experience that I want to pass along to the readers of this fine<br />

publication. I debated this process all during the beta and finally saw the light I was looking for.<br />

The basics<br />

Visual FoxPro comes with 29 base classes. These internally defined classes are provided by<br />

Microsoft and can't be modified directly. You can use these classes directly within an<br />

application, and you can subclass them. The conventional wisdom in the FoxPro community is<br />

that the base classes shouldn't be used directly in your applications. Instead, each should be<br />

subclassed, and you should use those subclasses (and other subclasses derived from them) in<br />

your applications (see Table 1). This will allow you to customize and standardize each class to<br />

have the functionality, look, and feel that you prefer. All the changes made to the subclassed


ase classes are then propagated throughout your applications using the power of inheritance.<br />

Table 1. Visual FoxPro base classes and standard name prefixes.<br />

Base Class Naming Prefix<br />

CheckBox chk<br />

Column* grc<br />

ComboBox cbo<br />

CommandButton cmd<br />

CommandGroup cmg<br />

Container cnt<br />

Control ctl<br />

Custom<br />

EditBox edt<br />

Form frm<br />

FormSet frs<br />

Grid grd<br />

Header * grh<br />

Image img<br />

Label lbl<br />

Line lin<br />

ListBox lst<br />

OLEBoundControl olb<br />

OLEContainerControl ole<br />

OptionButton * opt<br />

OptionGroup opg<br />

Page * pag<br />

PageFrame pgf<br />

Separator * sep<br />

Shape shp<br />

Spinner spn<br />

TextBox txt<br />

Timer tmr<br />

ToolBar tbr<br />

* These classes can't be subclassed in the Class Designerbecause they belong to a parent container.<br />

[There are also undocumented base classes that can be subclassed onlyprogrammatically with DEFINE CLASS:<br />

DataEnvironment, Cursor, and Relation.-- Ed.]<br />

There are definitely some things you'll want all of a specific object type to do that Microsoft<br />

didn't default to or provide. This is your chance to customize the different properties, events, and<br />

methods in the classes. One example is to base all the TextBox objects in an application on your<br />

personal TextBox base class. Microsoft set the default the text color to black. If you want all


TextBox objects on every form you create to have a blue foreground color, it's as simple as<br />

setting the ForeColor property to blue using Visual FoxPro's property sheet and color picker to<br />

modify your personalized base class. The inheritance built into Visual FoxPro will propagate the<br />

change to every TextBox object based on your TextBox class to blue. Another example might be<br />

to change the FontName or FontSize properties so all your form objects are consistent within an<br />

application (provided you don't break inheritance).<br />

How to subclass the base classes<br />

There are several ways to create a personalized set of base classes. I prefer to create them using<br />

the Visual Class Designer. Begin by typing the following in the Command window:<br />

CREATE CLASS<br />

You can also choose New from the File menu or the New icon on the standard toolbar to get the<br />

New File dialog box. Then select the Class option button and the New File command button to<br />

bring up the New Class dialog box (see Figure 1).<br />

The New Class dialog box is very straight forward. Fill in the name of your new class. The<br />

"Based On:" combo box is where you select the parent class to your personalized base class. The<br />

combo box drop-down list contains each of the Visual FoxPro base classes that can subclassed<br />

visually. Finally, type in the Visual Class Library filename or use the ellipse button to select an<br />

existing library. I recommend keeping all your personalized base classes in one Visual Class<br />

Library. Once you complete the dialog box, the Class Designer is started so you can modify your<br />

new class. Use the property sheet to alter any properties, events, or methods to your personal<br />

specification. This is very easy to do (don't worry, I won't tell your customers or the boss). I<br />

think it took me no more than five to 10 minutes initially to set up my current set of personalized<br />

"base" classes.<br />

Drag and drop is lost<br />

There is a downside to subclassing the base classes. One of the most frequently demonstrated<br />

and talked about features at DevCon 95 was drag and drop. You can quickly set up a form's data<br />

environment by adding tables from a database container or from free tables. You can then drag<br />

and drop fields from the tables in the data environment to the form. The newly added objects on<br />

the form will take the default base class for the field data type. Most fields default to the<br />

TextBox base class. Logical fields will default to the CheckBox base class, and memo fields will<br />

default to the EditBox base class. If you drag the table on to a form, you'll get a grid base class<br />

object. This is extremely slick and works as advertised.<br />

If you set up your own "base" classes (a visual class library of subclassed base classes), Visual<br />

FoxPro won't know to default these dragged objects to your own classes. Unfortunately, there's<br />

no way to get these objects to use your base classes without some customized Builder or Wizard.<br />

Such a routine would be fairly difficult to write, because it would have to actually change the<br />

class of the objects dropped, which would -- as far as I know -- require operating directly on the


.SCX file outside of the Form Designer. This would make seamless integration with the<br />

development environment difficult to achieve. This seems like a lot of work to me. I've spent<br />

enough time learning the basics of this product; I don't want to start writing utilities to change<br />

base class properties. I'm sure some developer will eventually come up with the solution, but I<br />

don't want to wait.<br />

So the choice is as simple as "drag and drop or customized base classes." It's an unfair choice,<br />

but one I think each developer will need to make at some point early in their Visual FoxPro<br />

apprenticeship.<br />

[Here's something to think about that might make the decision easier: since a data environment<br />

can be associated only with a form, saving a form as a class causes you to loose its data<br />

environment. In other words, data environments can't be visually subclassed, so you have to set<br />

one up for each individual form you create. Some developers feel this is enough of a liability that<br />

they are willing to forego the visual data environment designer altogether. Instead, they create<br />

DataEnvironment container classes programmatically, attaching code to each form's Load or<br />

Init events to manually instantiate the appropriate DataEnvironment object for the form. If you<br />

buy into this argument, then subclassing becomes almost a "no-brainer," since with this<br />

approach you can't drag and drop fields from the data environment anyway. -- Ed.]<br />

A solution finally realized<br />

I talked with several developers about this during the Visual FoxPro beta. The debates never led<br />

to a clear solution or reason to go one way or the other. It wasn't until I started developing some<br />

"production" screens that I finally realized why I wanted to create my own base classes. The<br />

reasons were two-fold (and in order of importance):<br />

• Microsoft defaults for properties and methods . I don't think every developer will<br />

agree on every default that Microsoft came up with for every property and method. I<br />

know I've changed a few basic properties. I've also added a few properties of my own,<br />

and I'm sure a method or two will be added over time as well. This is where the real<br />

power is.<br />

• Naming conventions . I was tired of each new object added to the form having names<br />

like Label1 or TextBox1. This required me to change the name to lblAddress or<br />

txtAddress in order to conform to Microsoft-recommended naming standards. I solved<br />

this by naming each of my base classes to conform to the Microsoft naming convention.<br />

For example, my base class TextBox is called txtTextBox. All I had to do is substitute<br />

"txtTextbox" for Microsoft's default name, "TextBox1." At design time, this results in<br />

TextBox names defaulting to txtTextBox1, txtTextBox2, and so on, a significant time<br />

savings. I also don't need to look up the standards in the help file when I forget them.<br />

Using your personalized base classes<br />

The classes are easy to create, but you might be asking "how easy are they to use?" Fortunately<br />

the Microsoft FoxPro team made using Visual Class Libraries as easy as they are to create. There<br />

is a View Classes icon (it looks like some books on a shelf) on the Form Controls toolbar. This


ings up a pop-up menu. Choose the Add menu option to add your personalized visual class<br />

library to the menu, then choose your library from the menu. The toolbar will change to reflect<br />

your base classes.<br />

You can set up Visual FoxPro to pre-load the View Classes menu with customized visual class<br />

libraries. Do this by using the Controls page of Visual FoxPro's Options dialog box (choose<br />

Options from the Tools menu; see Figure 2).<br />

Basic base class modifications<br />

I have made some basic modifications to my personalized base classes. The BackStyle property<br />

of Labels and CheckBoxes defaults to "0 - Opaque," which will drive you nuts if you've moved<br />

to a form color other than white. I've modified the BackStyle property of my Label and<br />

CheckBox base classes to "1 - Transparent." I don't really understand Microsoft's thinking on<br />

this one, but thankfully we have simple ways to make these changes.<br />

Another change that I find even more critical is my "base" class for Forms (called frmForm).<br />

This is my form Template Class that is set on the Form page of Visual FoxPro's Option dialog<br />

box (see Figure 3). Each new form created is based on (or, if you prefer, "inherits from") my<br />

template form class instead of the built-in Form class. This way, as I develop my forms and<br />

come up with my application development methodology, all I have to do is make changes in one<br />

place. If I add a custom property or method, each existing form based on my template form class<br />

automatically inherits the new value or functionality. [If you use FormSets, you might also want<br />

to create a template FormSet class. For detailed information on how form and FormSet<br />

templates interact, see the topic, "Using Form and Form Set Templates" in Visual FoxPro's help<br />

file. -- Ed.]<br />

Conclusion<br />

I decided that the "modify once, propagate everywhere" philosophy is the way to go. Losing drag<br />

and drop functionality was a small price to pay. I now add objects one at a time to my forms. I<br />

add an object, make any changes I need, and copy the first object over and over. For example, I'll<br />

add a TextBox object to my form and modify the appropriate properties. I then copy that object<br />

to the Clipboard and paste it on the form when I need another TextBox object. This procedure<br />

saves me time in the long run.<br />

Another concern I have with personalized base classes is adding another "layer" in the class<br />

hierarchy. I haven't performed, nor have I seen, an analysis of how this impacts runtime<br />

performance. Theoretically, it should slow performance slightly because Visual FoxPro has<br />

another layer to traverse as it looks for property values and to execute methods. [<strong>FoxTalk</strong> is<br />

preparing an article with benchmarks on this topic. -- Ed.]<br />

I hope this article has provided some insight on a topic that will be debated over and over as<br />

Visual FoxPro developers learn more about this exciting product. If it saves you time in making<br />

a decision or provides you with more arguments for going one way or another, then I've<br />

accomplished what I set out to do.


In download file _SCHUMME.EXE, I've provided the visual class library VF3BASEC, which<br />

contains all of the subclassed base classes that I use in my applications.<br />

Rick Schummer has been a FoxPro developer by day and night since 1989. He's also the president of the<br />

Sterling Heights Computer Club and a co-founding member of the Detroit Area Fox User Group. He<br />

enjoys camping and cycling. CompuServe 70254,1643.<br />

The Tracks of My Tiers<br />

Robert Green<br />

One buzzword making the rounds these days is "three-tier architecture." What is it, exactly, and<br />

what's it good for? And how do you implement it in Visual FoxPro?<br />

If you've been following developments in the client/server arena lately, you've no doubt heard<br />

about three-tier application development. It's a hot topic, and we should spend some time<br />

thinking about it. What is three tier? Can it be implemented in Visual FoxPro? How? And what<br />

about one tier and two tier?<br />

If buzzwords followed the same rules as software versioning, we would have moved directly<br />

from one tier to three tier. In fact, there really are three models. A one-tier architecture exists<br />

when the client and the server are the same environment. This describes the applications you<br />

build solely using FoxPro. The data, the forms it gets entered into, and the rules that control it<br />

are all written in the same place and run in the same place. Whether you access data directly<br />

from tables or through views and whether your rules are in code snippets or triggers, you have a<br />

single application layer: Visual FoxPro.<br />

In a two-tier architecture, the data is moved to another environment and is accessed through the<br />

first tier. This describes traditional client/server applications. The data resides in a back end such<br />

as SQL Server or Oracle. The front end is responsible for the user interface (the forms, menus,<br />

and so on). In a traditional two-tier architecture, the business rules are often enforced entirely on<br />

the back end.<br />

In a three-tier architecture, the business rules are stored in their own environment, typically on a<br />

separate computer so multiple front ends can use it. The front end supplies the interface, the back<br />

end supplies the data, and the middle tier takes care of enforcing the business rules. This means<br />

that you have two client/server conversations, one between the front end and the middle layer<br />

and one between the middle layer and the back end. The front end never talks directly to the<br />

back end.<br />

There are two main benefits to a three-tier architecture. The first is that you have more layers<br />

splitting up the work. You have three pieces to the application, user interface, data, and rules,<br />

and you have three layers to handle the chores. The second benefit is that it's easier to reuse your


usiness rules because they aren't buried in either the front or the back end. If you coded all of<br />

your data validation rules in Visual FoxPro, then it would be difficult to reuse them in, say, a<br />

Visual Basic or Access application. If they were sitting in their own layer, then any front end<br />

could use them to send data to any back end.<br />

This is the general idea of two- and three-tier architectures. In practice, you have more flexibility<br />

in how you structure your applications if you're using Visual FoxPro to build a client/server<br />

architecture. In a two-tier architecture, you can enforce the rules entirely in the back end, entirely<br />

in Visual FoxPro, or in both places. For example, if a rule is that prices can't go up by more than<br />

20 percent, you can do this in SQL Server with a rule or constraint. You can do this in Visual<br />

FoxPro in the Valid event of a text box.<br />

This is a pretty simple rule to enforce. If you enforce it in Visual FoxPro, the user gets instant<br />

feedback and the back end doesn't have to spend time checking the price. If you have 100 users<br />

entering 200 orders a day each, this adds up to a lot of work the back end doesn't have to do. If<br />

the only front end accessing the back end data is your Visual FoxPro application, then you might<br />

wind up putting the bulk, maybe even all, of the data validation code into the front end.<br />

But what if other front ends need to access the data? If Access and Visual Basic applications can<br />

enter or edit orders, then you would want to validate price on the back end. As a wise person<br />

once said, "Write once, use many." If the only other applications to access the data were written<br />

in Visual FoxPro, you could write FoxPro code and share it, perhaps through a stored procedure<br />

or a data validation class.<br />

In a three-tier architecture, you can still split the work by enforcing data integrity in the front<br />

end. If the middle tier is the only one responsible for making sure that price increases are<br />

acceptable, then are you overly burdening that layer? What will you gain by either duplicating or<br />

moving the rule enforcement into Visual FoxPro on the front end?<br />

What is the middle layer written in? The Visual Basic team wants you to write the middle layer<br />

in Visual Basic, using VB 4.0's new ability to create OLE servers. The OLE server could be<br />

called from Visual FoxPro using OLE Automation. Data would be passed to the middle layer,<br />

which would validate it. If the data were rejected, the middle layer would let FoxPro know about<br />

it. If the data were good, it would be sent on to the back end and FoxPro would be told.<br />

You would use this VB middle layer from Visual FoxPro the same way you use any OLE<br />

Automation server. You might issue the following to start a conversation with the server:<br />

oData = CreateObject("VBServer.BizRules")<br />

When you wanted to send data to the middle layer, you might issue the following code:<br />

oData.Send("Update employee Set salary = 87000000")


To see if the data was valid you might use the following code:<br />

If oData.ValidData<br />

= MessageBox("Success")<br />

Else<br />

= MessageBox(oData.ErrorMessage)<br />

Endif<br />

Note that the previous syntax is speculative. Your actual implementation may vary.<br />

What are the downsides to this approach? You won't be able to write client/server applications<br />

using Visual FoxPro's remote views, at least in this version of the product. Also, sending data via<br />

OLE should be slower than sending it via ODBC.<br />

You could write the middle layer in Visual FoxPro. When the front end wants to save data, one<br />

option is to store it in a table sitting out on the network somewhere. The middle layer is a Visual<br />

FoxPro application that constantly polls the table for new data to be validated. The data is either<br />

rejected or is sent to SQL Server. The middle layer could store a success or failure message in a<br />

table. The front end would then query that table and find out if the data it sent were valid or not.<br />

Compare that to writing the same validation rules in a stored procedure and putting that in a<br />

database. Each Visual FoxPro application could open that database and run the validation code<br />

in the front end. Only valid data is sent on to SQL Server. Does the three-tier architecture offer<br />

any benefit over this?<br />

I hope this column has raised some interesting questions about how to implement a three-tier<br />

architecture using Visual FoxPro. In the months to come we'll explore this issue further. In the<br />

meantime, let me know what your thoughts are. As always, FoxPro's the best. Pass it on!<br />

Robert Green is a vice president with The Information Management Group, a Chicago-based Microsoft<br />

Solutions Partner and Authorized Technical Education Center specializing in consulting and training in<br />

Microsoft database products. He was a speaker at the 1993 and 1995 International FoxPro Developer<br />

Conferences, and his articles have appeared in Data Based Advisor, FoxPro Advisor, and <strong>FoxTalk</strong>.<br />

312-280-1007, CompuServe 76104,2514, Internet rgreen@imginc.com.<br />

Tip: Get Object References by Location<br />

Ken Levy<br />

If you ever need read or write access to a visible object, either in design mode or in run mode,<br />

follow these steps:<br />

1. Activate the Command window<br />

2. Place the mouse over the object you want to get a reference to.


3. In the Command window, enter o=SYS(1270). The PUBLIC memory variable o is now a<br />

reference to the object.<br />

4. Address the object as needed. Example: o.Caption = 'Foo'<br />

5. When you're finished working with the reference, you should probably destroy the reference<br />

by executing o=.NULL. or o=.F. or RELEASE o. If the object you reference is a control in a<br />

container, you may not be able to release the container until all the references are released.<br />

[SYS(1270) and three other new SYS( ) functions are documented in the README.HLP file that<br />

comes with the Professional Edition of Visual FoxPro. -- Ed.]<br />

Ken Levy is a software engineer at Flash Creative Management Inc., a consulting and software<br />

development firm in River Edge, New Jersey. 201-489-2500, CompuServe 76350,2610.<br />

Do You Know Who Your Parents Are?<br />

Barbara Peisch<br />

Here's some hard-won wisdom in dealing with the idiosyncrasies of designing and using visual<br />

classes in the Class Designer and Form Designer. These workarounds will save you many hours of<br />

experimentation as you design increasingly elaborate forms.<br />

So, you're ready to start your first Visual FoxPro project. You've been reading all the articles and<br />

going to seminars, and you think you're prepared, right? Well, maybe not. There are many cases<br />

in Visual FoxPro where the default or intuitive approach either gives you something you didn't<br />

want or makes it impossible to easily make changes in the future. This article will point out some<br />

"gotchas" in building and using class libraries that you may not have heard about.<br />

Classy alternatives<br />

You may have heard that you should never instantiate the Visual FoxPro base classes directly. I<br />

agree with this because there's no way for you to change the properties of the Visual FoxPro base<br />

classes, and we all know how often you want to change things after you finish a program [for<br />

more information on subclassing the base classes and why you should, see "Subclassing Visual<br />

FoxPro Base Classes" by Richard Schummer, <strong>FoxTalk</strong>, November 1995. -- Ed.].<br />

In fact, I recommend a minimum of two levels of class libraries: first, a global class library that<br />

inherits directly from the Visual FoxPro base classes. You'll use this library to set standard<br />

attributes for all your applications. The second is a library for each client (or perhaps each<br />

application) you write. This gives you the ability to change a property for all instances of a<br />

particular object for that client or application without affecting every other Visual FoxPro<br />

application you've written. This may seem like unnecessary duplication at first, but you will be<br />

much happier when your client decides to change the behavior of all the text boxes in the<br />

application six months after you deliver the finished product.


Look Ma, no buttons!<br />

When you create your standard library, there are some things you will probably want to change.<br />

Some of these are a matter of taste, like the BackColor property of an object. Some you have<br />

probably heard are good practice, like making your forms use private data sessions. But there's<br />

one handy change you probably aren't aware of: changing the object count of container objects to<br />

zero. This includes the OptionGroup, CommandGroup, and PageFrame objects. (I didn't include<br />

FormSet in this list. We'll cover that later.)<br />

I can hear you saying, "Why would I want my base Option Button Group to have a button count<br />

of 0? I'll always want at least two buttons, so why not define the base class that way?" The<br />

reason for this is that you can't change the Name property of objects in the container that were<br />

instantiated from the parent class. In other words, you'll always have to refer to Options 1 and 2<br />

as "Option1" and "Option2" instead of "OptFile" or "OptDisplay." This may not seem like much<br />

at first, but when you have a lot of option groups nested deep in your container hierarchy, it will<br />

become a big deal.<br />

I'd like to give you a word of warning about making the PageCount of your PageFrame class 0.<br />

There's a problem with using an object based on the Container class when it's on a Page that<br />

didn't exist in the immediate parent class. The problem is that most, if not all, of the objects in<br />

the Container will either disappear or be strange sizes. (When I say "Container," I'm not referring<br />

to all container objects, like grids or OptionGroups. I'm referring only to the class called<br />

"Container.") To illustrate this problem, follow these steps:<br />

1. In the Class Designer, create a class based on the Container object. Put two labels and two text<br />

boxes in the Container and save the class.<br />

2. In the Class Designer, create a PageFrame class with a PageCount of 0.<br />

3. Create a new form.<br />

4. Create a PageFrame on the form by dragging your newly created PageFrame class from the<br />

toolbar.<br />

5. Change the PageCount to 2.<br />

6. Right click on the PageFrame and select "Edit." Select page 1 by clicking on it.<br />

7. Drag your new Container class from the tool bar and drop it on the page.<br />

8. Click on Page 2 and drop the Container object on it.<br />

You'll notice that you can't see the labels or text boxes in your Container on either page. Save<br />

this form and return to the Class Designer. Change the PageCount of your PageFrame to 1.<br />

When you modify your form in the Form Designer, you'll notice that the Container on page 1<br />

now looks fine but is still wrong on page 2. If you go back and increase the PageCount of the<br />

PageFrame class to 2, the second page will look fine.<br />

The point is your global PageFrame class can have zero pages. But if you plan to use a Container


on a page frame, you'll need a subclass with the pages that will hold Containers objects.<br />

Use SetAll to set common properties<br />

One problem you may have run into with container classes (I'm talking about all container<br />

objects now) is setting certain attributes for all the items you'll want to create in that container.<br />

For example, you may decide that you want the BackColor property of all the option buttons<br />

within your option group to be gray.<br />

You don't want to create an option group with the maximum number of buttons you'll need with<br />

the expectation that you can reduce the number of buttons when you instantiate the option group.<br />

Although you can remove these buttons with RemoveObject() at runtime, you can't reduce the<br />

number of controls at design time. You also don't want to go through the hassle of having to set<br />

the BackColor for each and every button in the group. This fails to take advantage of container<br />

polymorphism.<br />

Instead of laboriously setting a property for each object, you can use SetAll(). Put this nifty<br />

command in the Init method of the container to change a specific property for all of a particular<br />

kind of item in that group. For example, the following code will change the BackColor of all the<br />

options within a group to gray:<br />

THIS.SetAll('BackColor',RGB(192,192,192),'OptionButton')<br />

Now, don't worry when you drop an option group on your form and the BackColor on all the<br />

options is white. Remember, you don't actually run the Init code until you run the form. Any<br />

properties that you set in this manner won't be visible until runtime.<br />

Of course, it's possible to create a builder to set all the properties at design time, or even to use<br />

SetAll() from the Command window at design time if you first select the container and get a<br />

handle to the form with ASELOBJ():<br />

=ASELOBJ(o,1)<br />

o[1].SetAll('BackColor',RGB(192,192,192),'OptionButton')<br />

If you need to change this property, it's easier to go to the container's Init method and change a<br />

single runtime SetAll() call. Also, if you use the runtime SetAll() and later add more objects to<br />

the container, they will automatically share the properties of the existing buttons. You don't have<br />

to remember to change the properties for the new objects. Personally, I find these advantages<br />

outweigh the disadvantage of not being able to see the property settings at design time, but you<br />

may feel differently.<br />

Is your data dragging you down?<br />

You've probably seen at least one Visual FoxPro demo where someone dragged an item from the<br />

data environment and dropped it on the form to create a text box for that item. If you want to use


your own subclasses, don't do it like that. Text boxes you create that way will always be<br />

instantiated directly from the Visual FoxPro base class! The same goes for creating a grid by<br />

dragging the title bar of a table onto the form. If you don't believe me, just check the Class<br />

property of the text box or grid. And note that this class property is in italics, so you can't change<br />

it.<br />

A little discipline on your part will prevent such headaches. Whenever you create or edit a form,<br />

always do the following:<br />

1. Change the toolbar to show your own library instead of the Visual FoxPro base library. (No,<br />

Visual FoxPro won't always "remember" it from the time before.) To do this, click on the books<br />

icon. Then select your library if it's already shown, or choose "Add" and find your library.<br />

2. Always create objects on the form by dragging them from the tool bar. You can then change<br />

the ControlSource property for the item to whatever you want. Other techniques that work<br />

equally well are to drag-and-drop from either the project manager Class tab or the Class Browser<br />

onto the form.<br />

You can have your cake and eat it too by using Andy Griebel's GAPP utilities. GAPP will allow<br />

you to change the class property of objects after you put them on your form [see Whil Hentzen's<br />

"Cool Tool" column in the November 1995 issue for details. -- Ed]. This means that you can drag<br />

and drop from the DataEnvironment and use GAPP to change the class afterwards.<br />

Figuring out forms and FormSets<br />

Now, let's get back to the FormSet. Always have a least one form in each FormSet. Visual<br />

FoxPro won't let you set the FormCount to 0. Even worse, when using the Class Designer, the<br />

initial form created with a FormSet always inherits directly from the Visual FoxPro base class.<br />

This is true even if you have set a form template on the options screen.<br />

By creating your global FormSet with the Form Designer instead of the Class Designer, your<br />

initial form will inherit from your own Form class:<br />

1. Set a Form template to your base form class by selecting Options from the Tools menu. Do not<br />

set a template for a FormSet yet.<br />

2. Create a new form in the Form Designer.<br />

3. Click on the Form menu pad and select Create FormSet.<br />

4. Click on the File menu pad and select "Save as class." Make sure you select the option to save<br />

the entire FormSet. Select whatever name you want for your standard FormSet class.<br />

You can now set your FormSet template to your new template class.<br />

There is one disadvantage with this FormSet: You can't change the Name property of the form<br />

inherited from the parent class. You can set the Name property in your global FormSet subclass,<br />

and if you want the first form in all your FormSets to have that same name, then it's not a<br />

problem. If you want to assign new names to the first form on each FormSet subsequently


created, however, you must use only forms added in the Form Designer. To remove the original<br />

form (which you can't rename), use "THIS.RemoveObject('')" in the Init method of<br />

your FormSet to remove the form you can't name. To make sure you don't accidentally use this<br />

form in the designer, change its size to the minimum in the Class Designer and move it down to<br />

the lower right corner of the FormSet; otherwise, you'll end up putting objects on this form and<br />

wonder where they went when you run it.<br />

Don't forget that if you want Visual FoxPro to use your form subclass when you create a new<br />

form, you must go to the "Tools...Options...Forms" screen to specify the class to use for forms.<br />

Make sure you press the "Set as Default" button before you exit if you want this option to be<br />

restored the next time you run Visual FoxPro. You may notice that there are two sections on this<br />

screen, one for your default FormSet and one for your default form.<br />

If you define both a default FormSet and a default form, the FormSet template will be the one<br />

used when you create a form. If you want a single form, first add a new form (so you can name<br />

it). Then remove the first form and the FormSet. If you plan on using FormSets, you should set<br />

templates for both the FormSet and Form so that Visual FoxPro will use your form class when it<br />

adds forms to FormSets.<br />

Avoiding gridlock<br />

Grids can be a little time consuming to set up properly. The problem is that the control for<br />

columns in a grid will by default be the Visual FoxPro base TextBox class. Telling Visual<br />

FoxPro which library to use for its grid text boxes is one thing you can't do with a call to SetAll<br />

in the Init method as I discussed earlier. This is because all the items in the container are<br />

instantiated by the time you run the container's Init method. Once an item is instantiated, you<br />

can't change its class.<br />

If you create a grid with a column count of -1, you'll have no control over naming column<br />

headers or what controls go where. So, although there's no problem in your grid's subclass<br />

having a column count of -1, you'll want to change that when you actually use the class at design<br />

time.<br />

First, get around the problem of the base TextBox class being used in grids by deleting these text<br />

boxes and putting in your own controls. The only way to delete the text box is to select the text<br />

box from the property sheet, click on the title bar of the form or the grid itself (make sure you<br />

don't click outside the grid), and press the delete key. You should see the TextBox icon<br />

disappear from the grid column. At this point you can drag any control from your toolbar and<br />

drop it on the column.<br />

You can drop multiple controls in the same column. Only one control can be visible at a time, as<br />

determined by the CurrentControl property for the column.<br />

Conclusion<br />

I hope I've given you some techniques that will make your life easier when using Visual FoxPro.<br />

Most of the tidbits in this article came from many hours of frustration and help from fellow


programmers. I hope my hours of exploration will save you some time.<br />

Barbara Peisch is a partner with Gersztyn, Peisch and Associates, which specializes in custom software.<br />

She has been with the firm since 1988. 619-268-4639, CompuServe 70303,1064.<br />

Ignore Deleted Records, Set Form Template<br />

Defaults, and More<br />

John V. Petersen<br />

My application runs with SET DELETED OFF, but I want to ignore deleted records in a<br />

particular form. I issued the command SET DELETED ON set in the Load method of my<br />

form, but the views in my Data Environment still includes the deleted records in the result<br />

set. It's my understanding that the Load method is the proper place to put my data<br />

session-specific SET settings. In addition, I'm using private data sessions. What am I<br />

missing?<br />

The best answer depends on whether you use the DataEnvironment. If so, the first event that<br />

fires is actually BeforeTablesOpen. This event belongs to the DataEnvironment, not the form. By<br />

the time the form's Load method fires, the query associated with the view has already been<br />

executed. The problem is that your SET DELETED setting was still OFF when<br />

BeforeTablesOpen fired, which means that your query saw the deleted records and brought them<br />

into view.<br />

One solution is to place your SET settings in the BeforeTablesOpen method of the<br />

DataEnvironment. If you don't want to put your code there, you can take the following steps:<br />

1. Set the NoDataOnLoad property of the view cursor object to .T.<br />

2. In the Load method of the form, enter SET DELETED ON.<br />

3. In the Init method of the form, make a call to the REQUERY() function. This causes the query<br />

to occur after you SET DELETED ON.<br />

I like the idea of form template defaults that can be set in the Forms Section of the Options<br />

dialog box under the Tools menu. However, it becomes quite burdensome when I have<br />

multiple forms classes and have to continually reset the template Visual FoxPro uses. Is<br />

there an easier method?<br />

I'll outline two solutions: one prescription is for folks who have the Professional Edition and the<br />

Class Browser, and the other is for folks who use the Standard Edition. First, the solution for the<br />

Standard Edition users (this will also work in the Pro Edition as well):<br />

The trick is to employ a pre-3.0 FoxPro trick: hacking the .SCX file. Every object on a form has


a record in the .SCX. Two fields in the .SCX that are of interest here are the Class and Classloc<br />

fields. By default, all forms created are based on Visual FoxPro's base class definition.<br />

Therefore, the default values of Class and Classloc are Form and an empty string, respectively.<br />

Assume that your class definition, called 'MyForm', is contained in 'MYLIB.VCX'. Further<br />

assume that MYLIB.VCX is contained in the same directory as your .SCX. It's merely a task of<br />

changing the values of Class and Classloc to 'MyForm' and 'MYLIB.VCX', respectively. When<br />

changing the Classloc value, make sure to enter the relative path if the class library isn't in the<br />

current directory. Finally, close the .SCX and open the form. Your form should now be<br />

inheriting from your MyForm class.<br />

For those of you who have the Professional Edition (and therefore the Class Browser), the<br />

procedure is a bit simpler. First, open the Class Browser and instead of opening a class library,<br />

open your form. Next, with the form reference highlighted in the outline control of the browser,<br />

press the Redefine button. You'll now be presented with a dialog box to enter a class definition<br />

and the visual class library in which the definition resides. In this example, it will be Redefine<br />

As 'MyForm' Of 'MYLIB.VCX'. Finally, press the Redefine button to save your<br />

changes. The form will now inherit from the 'MyForm' class.<br />

In both cases, I encourage you to make a backup of your form so that you can recover from any<br />

mistakes.<br />

I have a form that uses a series of tables and views. All of the cursor objects in the Data<br />

Environment are set to optimistic table buffering. Additionally, all of the views have the<br />

Send SQL Updates check box checked. Whenever I make changes to a view and issue a<br />

TABLEUPDATE() and subsequently close the form, I get an uncommitted change error<br />

referencing the table that is associated with a view even though a TABLEUPDATE() was<br />

issued. What could possibly be the problem?<br />

The behavior you're encountering actually makes sense when you break the steps down. Views<br />

will respect the buffers of the tables upon which they are based. Since the tables that your view<br />

is based on are already open and are in optimistic buffer mode, when you update a view it<br />

updates the underlying table's buffer, not the disk. Therefore, you must make a final pass through<br />

your tables to commit any changes that have been made. Code similar to the following will do<br />

the trick: (Assume the name of the view is called 'myview' and the table upon which it is based is<br />

called 'mytable'):<br />

BEGIN TRANSACTION<br />

IF tableupdate(.t.,.t.,'myview') AND ;<br />

tableupdate(.t.,.t.,'mytable')<br />

END TRANSACTION<br />

ELSE<br />

** tell the user there is a problem<br />

ROLLBACK<br />

ENDIF


Is there a way to ensure my modal forms will always retain their modal status regardless whether<br />

I generate the screen inside or outside the project?<br />

Sometimes, it's faster to generate one screen than to rebuild the project to see your results. The<br />

best solution is to use the #READCLAUSES MODAL screen generator directive in the Setup<br />

Snippet of the screen. This will ensure that whenever screen code is generated, the READ<br />

command will always have its associated MODAL clause.<br />

John V. Petersen, MBA, is director of FoxPro and Visual FoxPro development for MaxTech Inc., a<br />

consulting firm specializing in database consulting, project development and mentoring, and training<br />

based in Northern Virginia. Working out of Philadelphia, John is active in the FoxPro community, has<br />

written for publications in the United States, and has been a speaker at user group meetings and at the<br />

1995 Developer Days Conference. CompuServe 72722,1243.<br />

Tip: Avoid Two-Dimensional Arrays<br />

Barry Chertov<br />

Many people use two-dimensional arrays with different types of data in each column and use<br />

constants to identify each column, for example:<br />

#DEFINE C_StateID 1<br />

#DEFINE C_Population 2<br />

#DEFINE C_StateFlower 3<br />

DIMENSION StatePop[50,C_StateFlower]<br />

This leads to all sorts of trouble. The first problem is that #DEFINEd constants aren't useable in<br />

the debug window. To solve this, you could be a heretic and use variables that happen to be<br />

constant.<br />

The second problem is that searching is a nightmare. You have to use ASCAN, which doesn't<br />

know about columns. This means it's slower because all the elements are searched, not just the<br />

column you're interested in. At best, it's slow and you then have to use AELEMENT() to get the<br />

row and column number. At worst, you could get misled if the search happens to match on a<br />

unintended column.<br />

Here's the answer: It's a database system! Use a cursor. You can index and search easily using<br />

familiar commands. A two-dimensional array can be useful if it holds only one kind of<br />

information (such as Sales[year,month]).<br />

Barry Chertov lives in Sebastopol, California, and is the primary developer of Foxfire!, a query and<br />

reporting add-on for FoxPro 2.x and Visual FoxPro. CompuServe 73020,1032.


A Glossary of Object-Oriented Terminology<br />

for FoxPro Developers<br />

Ken Levy<br />

Dense terminology is one of the biggest impediments to entry into the object-oriented (OO) world.<br />

This glossary eases the problem by relating the terms to their implementation in Visual FoxPro.<br />

In my article, "Object Oriented Programming: Preparing for a New FoxPro Paradigm" (<strong>FoxTalk</strong>,<br />

October 1994), I laid a conceptual groundwork for object-oriented programming (OOP) for<br />

FoxPro 2.x developers. That article ran almost three months before the Visual FoxPro beta was<br />

widely available and nine months before the first commercial release of the product. Now, some<br />

six months after commercial release, a second major wave of Visual FoxPro developers are<br />

wading deep into their first significant Visual FoxPro projects. If you didn't see the original<br />

article, you might want to call Pinnacle at 800-788-1900 and order a copy of the October 1994<br />

issue.<br />

In the meantime, here's a glossary of terms that didn't run with my original article due to lack of<br />

space. Although you can learn this terminology from any OOP text, I've optimized this glossary<br />

for the Visual FoxPro developer and covered the terminology quirks specific to Visual FoxPro -something<br />

I wasn't at liberty to do when the original article ran.<br />

Abstract data type<br />

An abstract data type is a data type defined by the programmer that isn't supported by the<br />

language. In non-OOP languages, such as standard C, abstract data types can be constructed<br />

from existing data types that the language recognizes (in C, this is done using language features<br />

such as typedef or struct). In OOP languages, abstract data types are generally high-level<br />

definitions within a program, called classes, from which runtime objects are created that<br />

correspond to real world objects. In Visual FoxPro, this latter type of data abstraction is<br />

supported programmatically through DEFINE CLASS and visually with the Class Designer<br />

(which is usually called from the Class Browser or Project Manager). Runtime objects are<br />

referenced via memory variables of type "O," which serve as references (or pointers) to objects.<br />

Abstraction<br />

Abstraction has the benefit of ignoring irrelevant details of software components and focusing on<br />

only those details required to interface, send messages, and so on. Abstraction includes concepts<br />

such as information hiding and black box routines. Since most real world objects include abstract<br />

details, objects allow a better representation of real world objects than functions do.


Base class<br />

Base class is the root class or most generalized class in a class hierarchy. In the FoxPro world,<br />

the term is used more restrictively to mean the "built-in" classes from which all other classes<br />

must be derived. Examples of base classes in Visual FoxPro include Form, Custom, and<br />

CommandButton.<br />

Class<br />

Class is a blueprint, or template, for defining methods and properties of a particular type of<br />

object. Classes are defined at design time and aren't executed at runtime. Objects are created<br />

based on class blueprints at runtime. Multiple objects can be created (instantiated) from a single<br />

class at runtime. Classes are defined programmatically with the DEFINE CLASS command, or<br />

visually using the Class Designer. Objects are created with the CREATEOBJECT() function.<br />

Class hierarchy<br />

Class hierarchy is a tree structure of classes that represent the relationship between a set of<br />

classes of similar type. The top node of a class hierarchy is known as the base class or<br />

superclass, while related classes below it are known as subclasses. Although you can create any<br />

number of levels of subclasses, most methodologies state that well-defined class hierarchies<br />

shouldn't exceed five levels deep.<br />

Note here an important quirk in Visual FoxPro OO terminology: Outside the FoxPro world, the<br />

term base class means the root or top class in a class hierarchy; in Visual FoxPro documentation,<br />

"base class" means one of the "built-in" classes from which all user-defined classes are derived.<br />

If you're reading OO literature that isn't FoxPro-specific, keep in mind that the term "base class"<br />

is used less restrictively than the FoxPro documentation usually uses it.<br />

Composite (container) object<br />

A composite, or container, object is an object that contains one or more objects. This occurs<br />

when at least one instance variable of an object is of type object (in Visual FoxPro, it also occurs<br />

when an object is defined as a container for other objects; see Instance). Composite objects,<br />

along with delegation, are typical techniques to eliminate the unwanted side effects of multiple<br />

inheritance (see Inheritance).<br />

Delegation<br />

Delegation occurs when an object receives a message and passes it on to another object.<br />

Delegation is useful in "chain of responsibility" situations where an object handles only those<br />

messages it understands, and simply passes on messages it doesn't understand to some other<br />

object. In effect, it delegates responsibilities to other objects. Non-OOP languages can also<br />

support delegation by defining a user-defined function (UDF) that passes its parameters on to<br />

another UDF. The interface to the developer sending the message doesn't have to know about the<br />

internal details.


Encapsulation<br />

Ecapsulation is the concept of methods (procedures or functions) and properties (data memory<br />

variables) packaged together. The result of this packaging is the object. Objects contain both<br />

methods and properties. The methods are referred to as the object's behaviors and the properties<br />

are referred to as the object's attributes.<br />

Inheritance<br />

Inheritance occurs when a class is derived based on an existing class; the existing methods and<br />

properties are referenced for reuse with the new class. A subclass uses all of the methods and<br />

properties defined in the superclasses above it within the class hierarchy. Inheritance can be<br />

broken for individual methods and properties when creating a subclass. This is known as<br />

specialization.<br />

Generally the term "inheritance" implies single inheritance. In some languages, such as C++,<br />

multiple inheritance is supported. In multiple inheritance, a single subclass can be derived from<br />

more than one superclass. Along with the problems of naming conflicts within the union of<br />

methods and properties of the superclasses, multiple inheritance is considered by many software<br />

engineers to be an improper class hierarchy design solution because the combination of<br />

composite objects and delegation is superior to multiple inheritance. Visual FoxPro doesn't<br />

support multiple inheritance.<br />

Instance<br />

Instance is a term that describes a runtime occurrence of an object belonging to a particular class.<br />

Classes are defined at design time, while object instances are created from defined classes at<br />

runtime (in Visual FoxPro, using the CREATEOBJECT() function). An object member within a<br />

container is also an instance (objects can be added to a container at design time with the ADD<br />

OBJECT clause of DEFINE CLASS; they can be added at runtime with the container object's<br />

AddObject() method).<br />

Message<br />

A message is an instruction from one object (the sender) to another object (the receiver) to<br />

execute one of the receiver's methods. The three parts of a message are defined by the receiver<br />

object name, the method name, and any parameters the method might require. Messages can also<br />

be two-way, which occurs when the receiver object's method returns a value to the sender:<br />

* Sending a message to an object<br />

oRegistry.RequestUpdates(THIS)<br />

* Sending a message to an object and receiving a response<br />

lUpdatesOn = oRegistry.RequestUpdates(THIS)<br />

In these examples, oRegistry is the reference to the object, RequestUpdates is a method of


oRegistry, and THIS is a message being sent to oRegistry (in this case, a reference to the object<br />

sending the message). In the second example, lUpdatesOn receives the return message as a<br />

logical value (true if the RequestUpdates method successfully handled the request).<br />

Object<br />

An object is a software collection of related methods (functions) and data (memory variables).<br />

The methods generally act on the associated data. An object generally refers to an instance of a<br />

class.<br />

Parent class<br />

Visual FoxPro documentation uses the term parent class to mean the same thing that most OO<br />

texts refer to as a superclass. See the definition for superclass.<br />

Polymorphism<br />

Polymorphism is the ability to send the same message to various objects while each object is<br />

responsible for acting on the common message. Polymorphism allows a common interface to be<br />

created for objects sending messages. For example, sending the message "Draw" to both a box<br />

object and a circle object would be an example of polymorphism since the message that was sent<br />

to each object had the same name while each object contained its own Draw method. A Draw<br />

method would be defined in both the Box class and the Circle class and each may have similar or<br />

completely different implementations. Polymorphism is supported by allowing the same method<br />

name to be defined within different classes.<br />

Superclass<br />

A superclass is a class that is the parent of one or more subclasses.<br />

Ken Levy is a software engineer at Flash Creative Management, Inc., a consulting and software<br />

development firm in River Edge, New Jersey. Ken spends most of his time working in the Los Angeles<br />

area, specializing in the research, design, and creation of application development tools. While working<br />

as a consultant full-time at NASA's Jet Propulsion Laboratory in Pasadena, Ken developed several public<br />

domain programs including GENSCRNX, PTX Editor, and other FoxPro tools. 201-489-2500,<br />

CompuServe 76350,2610.<br />

Save Space in Memo Fields<br />

Bob Grommes (3)<br />

Here's an overview of how block sizes affect memo field size and fragmentation in both Visual<br />

FoxPro and FoxPro 2.x, plus the scoop on a little-known Visual FoxPro enhancement to memo<br />

fields and some coding tips to reduce memo field "bloat."


Memo fields (variable-length fields in the parlance of many languages) have been with us for a<br />

long time in the Xbase world. Old-timers will recall that they weren't very useful back in the<br />

pre-FoxPro days. One of the most exciting things about FoxPro 1.0 was that memo fields could<br />

handle binary data of arbitrary size, and not just text. Not only that, but we finally got some<br />

useful tools and commands for manipulating memo field data.<br />

But there are some interesting benefits to understanding how FoxPro implements memo fields.<br />

Applying your knowledge of what goes on "behind the scenes" can result in significant,<br />

sometimes dramatic, disk space and efficiency savings, as I'll demonstrate.<br />

Memo field data is stored in an .FPT memo file, which is completely separate from the .DBF<br />

table file. In the .DBF, a pointer is stored to the physical location of the memo data in the memo<br />

file. At that location in the memo file, FoxPro sees eight bytes of data that indicate the number of<br />

blocks the memo data occupies (almost always one block), and the length of the first memo<br />

block. So far, so good.<br />

But FoxPro introduces a wrinkle. When a table is created, the .FPT file is stamped with a<br />

minimum memo field allocation for each value stored. This "memo block size" is determined by<br />

the SET BLOCKSIZE command in effect at the time the table is created.<br />

The SET BLOCKSIZE command is an odd beast, because like many parts of the FoxPro<br />

language, it's saddled with backward compatibility issues. In dBASE III Plus, you could SET<br />

BLOCKSIZE to any value between 1 and 32. If you SET BLOCKSIZE TO , dBASE would<br />

use a block size of times 512 bytes.<br />

This meant that even if you SET BLOCKSIZE TO 1, the minimum value, a minimum of 512<br />

bytes was always allocated in the memo file for the contents of any memo field -- an<br />

extravagance in the days of 20M hard disks!<br />

FoxPro 1.0 extended SET BLOCKSIZE so that if you used a value of 33 or greater, it was<br />

interpreted in bytes instead of 0.5K units. This effectively gave us block sizes ranging from 33<br />

bytes and upwards. The default value for SET BLOCKSIZE in FoxPro is, and always has been,<br />

64.<br />

A hidden treasure<br />

You may not have noticed the new ability to set the memo field block size to "zero" in Visual<br />

FoxPro, using the command SET BLOCKSIZE TO 0. When you SET BLOCKSIZE TO 0,<br />

Visual FoxPro effectively uses a block size of 1 byte -- in other words, the contents of memo<br />

fields can now take up only as much space in the .FPT file as the data demands. More accurately,<br />

a table created while SET BLOCKSIZE TO 0 in effect stores each memo field in a single block<br />

of exactly LEN() + 8 in size. In effect, there is no minimum allocation for a block<br />

of memo data.<br />

For example, consider an .FPT with the following two memo field values stored in it:


This is a test.<br />

X<br />

Ignoring the 512-byte header that appears once at the very beginning of any .FPT, the .FPT<br />

storing these two strings takes up 32 bytes if you SET BLOCKSIZE TO 0 before creating the<br />

table, but takes 82 bytes if you SET BLOCKSIZE TO 33. With the default SET BLOCKSIZE<br />

TO 64, it takes 128 bytes. Clearly, with thousands of records, smaller block sizes can add up to<br />

big space savings.<br />

Incidentally, regardless of the block size setting, there is one more improvement in Visual<br />

FoxPro memo fields. The memo field pointer in a FoxPro 2.x .DBF used to take 10 bytes per<br />

record, whether or not the memo field contained data; in Visual FoxPro, it takes only four bytes<br />

per record. That's a savings of six bytes per record, or about 1K times the number of memo fields<br />

for every 170 records in a table.<br />

What's the best size?<br />

Should you always SET BLOCKSIZE TO 0 in Visual FoxPro (or to 33 in FoxPro 2.x)? Probably<br />

not, but these are generally the best defaults to use. The trade-offs in memo field sizes have to do<br />

with FPT fragmentation.<br />

I've described the "ideal" situation that exists when a table contains nothing but newly added,<br />

unedited records. The trouble begins when you start editing memo fields. Any time you replace<br />

the existing contents of a memo field with a longer string than the original, FoxPro abandons the<br />

original block and rewrites the entire string at a new location in the .FPT. The more often this<br />

happens, the more wasted space in the .FPT, and it's reclaimable only via PACK, PACK MEMO,<br />

or by copying the table. On the other hand, regardless of memo block size, once a block of memo<br />

data is written to the .FPT, values that are the same length or shorter will reside in the same area<br />

and the only wasted space will be the difference between the lengths of the original and the new<br />

values.<br />

It's important to realize that block sizes have to do only with minimum allocations for memo<br />

field data. For example, if you SET BLOCKSIZE TO 64, that doesn't mean that a very long<br />

memo field value will be broken up into dozens of 64-byte blocks that are chained together<br />

through a pointer list; a large memo will be physically stored in a single block. SET<br />

BLOCKSIZE TO 64 just means that 64 - LEN() bytes will be "wasted" if the<br />

memo field value is less than 64 bytes long. But it also means that you can REPLACE any<br />

values you want into the memo field, as long as the values are 64 bytes or less in length, and the<br />

memo data won't be "moved" to a new block, thereby "orphaning" the entire original 64-byte<br />

allocation.<br />

How do you determine the best memo block size for any given table in your application?<br />

Smaller block sizes are ideal for tables where the memo field contents aren't changed frequently,<br />

are generally short in length, or for smaller tables that can be packed frequently. For example,<br />

Visual FoxPro uses a block size of 1 byte when creating forms (SCXs) and class libraries


(VCXs), regardless of what you currently have the memo field block size set to. These small<br />

tables are frequently rewritten and can be packed quickly, and the memo fields usually contain<br />

short strings. Furthermore, SCXs and VCXs are often embedded in .APP or .EXE files, where<br />

space is at a premium. I suspect this latter consideration may have had a lot to do with inspiring<br />

the new SET BLOCKSIZE TO 0 option and the new, more compact memo pointer in the .DBF.<br />

(Incidentally, unlike SCXs and VCXs, database containers (DBCs) are created with whatever<br />

block size is in effect at creation time, just like any other table, while report form tables (FRXs)<br />

are always created with a block size of 33).<br />

Consider larger block sizes when you know that most of your memo field data will consist of<br />

longer strings, or will be changed frequently with data of varying widths.<br />

Analyzing application data<br />

In general, if a table has a single memo field, and you have some idea of what the average length<br />

of the data going into that field will be (ignoring records with no data in the field), then that's<br />

probably your ideal memo block size for that table. The smaller the percentage of records that<br />

actually have memo field data, the closer the block size can afford to approach the maximum<br />

length that the field will ever store.<br />

Often, it's best to let a new application run in the field for awhile and accumulate a significant<br />

amount of data. You can then analyze the contents of the memo fields and adjust the block size<br />

by using SET BLOCKSIZE followed by COPY to create a new table with a more efficient FPT.<br />

If the table isn't too big, you might consider making several copies of it with different memo<br />

block sizes and using the setting that produces the smallest FPT.<br />

However, for an accurate comparison, first do a PACK MEMO on the original table to remove<br />

any fragmentation. Not only will your comparisons be more accurate, but the degree of<br />

fragmentation will be instructive. Even if a particular block size initially yields the best<br />

efficiency, a larger block size may help prevent fragmentation as the file is used use over time.<br />

Things get more complicated if there are multiple memo fields in the table with varying<br />

requirements. Regrettably, the memo field block size is an attribute of the table, not of individual<br />

memo fields. In such situations you'll have to weigh the requirements of each memo field<br />

individually, weight them according to frequency of usage, and come up with a good overall<br />

block size for the entire table.<br />

Block size-related commands<br />

SET BLOCKSIZE doesn't alter the block size of existing tables; it simply governs the block size<br />

of any tables that you create. Any time your application is about to create a table (CREATE<br />

TABLE, COPY, and similar commands), consider what you expect to see in the table's memo<br />

fields and consider issuing an appropriate SET BLOCKSIZE command first.<br />

You can determine the memo block size (in bytes) of existing tables using the SYS(2012)<br />

function against the table once it's open (SYS(2012) returns zero if the table has no memo<br />

fields). You can use SET(`BLOCKSIZE') to determine what your current SET BLOCKSIZE


setting is (in bytes).<br />

A likely problem and its solution<br />

Here's one caveat to observe: the SET(`BLOCKSIZE') function always returns the current block<br />

size setting in bytes. If, for example, you SET BLOCKSIZE TO 1, SET('BLOCKSIZE') will<br />

return 512. If you're changing the block size in a routine and want to preserve and restore the<br />

caller's block size (a technique often used in application startup and shutdown code as well), you<br />

run into a problem. Consider the following code:<br />

nOldBlockSize = SET('BLOCKSIZE')<br />

SET BLOCKSIZE TO 64<br />

* more code here, then reset the caller's block size<br />

SET BLOCKSIZE TO (nOldBlockSize)<br />

If the caller had previously SET BLOCKSIZE TO 0, then SET('BLOCKSIZE') will return a<br />

value of 1, since SET BLOCKSIZE TO 0 produces one-byte blocks. But when you "restore" the<br />

caller's block size, you'll issue SET BLOCKSIZE TO 1, which will create 512-byte blocks, just<br />

like in the good old days of dBASE III Plus! To get around this problem, you need code like the<br />

following instead:<br />

nOldBlockSize = SET('BLOCKSIZE')<br />

IF nOldBlockSize = 1<br />

nOldBlockSize = 0<br />

ENDIF<br />

SET BLOCKSIZE TO 64<br />

* more code here, then reset the caller's block size<br />

SET BLOCKSIZE TO (nOldBlockSize)<br />

REPLACE and fragmentation<br />

A technique you can use to minimize .FPT fragmentation regardless of memo block size is to<br />

avoid repeated REPLACEs on a memo field whenever possible. For example, suppose that you<br />

have code that appends transaction log information to a memo field named TranLog. Perhaps<br />

you need to add several lines to the field. You might have code similar to the following:<br />

FOR n = 1 TO nMessages<br />

REPLACE TranLog WITH CHR(13) + cTransMsg[n] ADDITIVE<br />

ENDFOR<br />

This code extends the memo field on each iteration of the loop, potentially orphaning .FTP file<br />

space each time. It's much better, and probably faster, to build the additional data in memory and


do a single REPLACE:<br />

cNewMessages = ''<br />

FOR n = 1 TO nMessages<br />

cNewMessages = cNewMessages + CHR(13) + cTransMsg[n]<br />

ENDFOR<br />

IF NOT EMPTY(cNewMessages)<br />

REPLACE TranLog WITH cNewMessages ADDITIVE<br />

ENDIF<br />

I once had a text management application that would read in text files, do on-the-fly translations,<br />

and write the text files a line at a time to a memo field. I noticed that the memo file was bloating<br />

by a factor of several hundred K per day, even though no new records were being added to the<br />

table. This coding trick got rid of most of the "bloat."<br />

Conclusion<br />

Fiddling with memo block sizes isn't one of the most pressing issues in FoxPro database<br />

development, but as you deal with larger and larger databases, some attention to detail in this<br />

area can have a significant impact on disk storage efficiency. And, as you saw in the code<br />

examples I just shared, paying attention to the subtleties of syntax can produce big savings, too.<br />

Finally, I suspect that Visual FoxPro is taking us in directions that are likely to cause us to<br />

provide database administration services to clients in addition to application design and<br />

development services. Knowing how to tune memo fields is one skill that will differentiate the<br />

kind of administration you can provide from what the client can do in-house.<br />

Bob Grommes is editor of <strong>FoxTalk</strong> and president of Software Excellence in Kalamazoo, Michigan.<br />

CompuServe 74126,72.<br />

Ease Form Design Tasks with SUPERCLASS<br />

Whil Hentzen (4)<br />

GENSCRNX fans probably wonder what Ken Levy has been up to since the release of Visual<br />

FoxPro. Wonder no more: he's produced another "must have" that every Visual FoxPro developer<br />

will want in his bag of tricks.<br />

Ever had one of those days? The type of day where nothing goes right? You're working in Visual<br />

FoxProand you have to look at the Language Reference for just about every command you type.<br />

For some reason, you can't remember the syntax for even the simplest commands, like WAIT<br />

WINDOW or SKIP. You even tried to do some simple data entry in a Browse window, but<br />

couldn't figure out why Ctrl-N wasn't working when you were adding new records.


Of course, according to Murphy's Law, you probably have a ton of class and form design work to<br />

do on this type of day. As a result, you keep running into that annoying problem of not being<br />

able to work with a class and a subclass, or a form and the class used to create objects on that<br />

form. Typically, the following happens:<br />

1. Grab a class library and place it on the Form Controls toolbar.<br />

2. Create a form, and place some controls on the form using classes in the class library.<br />

3. Realize that you have to change something about the class -- some property, say, or perhaps<br />

you have to add a method.<br />

4. Try to modify the class, and face that obnoxious error message: "Cannot modify a class that is<br />

in use."<br />

5. Close the form.<br />

6. Modify and save the class.<br />

7. Open up the form again.<br />

8. Try to remember what it was you were doing in the form that required changes to the class.<br />

You get this message because the appropriate record in the class library file (the .VCX table) is<br />

locked when an instance is open in the designer.<br />

SUPERCLASS to the rescue!<br />

Since there's no need for GENSCRNX 3.0 in Visual FoxPro, Ken Levy has spent his time<br />

reading the minds of frustrated developers who are "having one of those days." He created<br />

SUPERCLASS, a utility that enables you to view and edit the methods of an object's superclass.<br />

You'll still have to close the form or class if you want to do other things (like modify properties<br />

of a superclass), but SUPERCLASS will save you hours of opening and closing designers simply<br />

to tweak the method of a superclass.<br />

Using SUPERCLASS is one of those operations that takes longer to explain than it takes to do.<br />

First, run SUPERCLS.PRG. I recommend you put SUPERCLASS in your developer utilities<br />

directory, or somewhere else in your path and run it during Visual FoxPro startup.<br />

SUPERCLASS stays hidden until you open up a method-editing window in the Form Designer<br />

or Class Designer. At this point, the SUPERCLASS toolbar with three buttons appears (see<br />

Figure 1).<br />

SUPERCLASS functionality<br />

The left button allows you to view or edit the superclass method of the object that you are<br />

working onprecisely what you weren't able to do before. Furthermore, it's just a mouse-click<br />

away; you don't have to open up the Class Designer.<br />

However, there's a caveat to be aware of when editing superclass methods. Behind the scenes,<br />

SUPERCLASS makes a temporary .VCX with the record of the Methods field. When you


change the superclass method, those changes are placed in this temporary file. The temporary<br />

file is then closed, recompiled, and the resultant ObjCode field contents are placed in the .VCX.<br />

However, the instance of the original object is still in memory, and this doesn't get changed. As a<br />

result, if you create more instances of that class, these new instances will still contain the old<br />

method code. To prevent this from happening, you'll have to CLEAR CLASS (or CLEAR ALL,<br />

and DO SUPERCLASS again).<br />

The middle button on the SUPERCLASS toolbar allows you to insert a superclass method call in<br />

the current method window at the current cursor position. However, if you're inclined not to read<br />

the documentation, you'll miss a trick. Left-clicking will insert a call to the superclass method. In<br />

other words, suppose you have a command button class called cmdBase, and in the Click()<br />

method of that button, you've placed the following code:<br />

WAIT WINDOW "Jeepers. I'm in the Click method"<br />

If you place an instance of that command button class on a form, opening up the Click() method<br />

of the command button instance will bring forward the SUPERCLASS toolbar. Clicking on the<br />

middle button will insert the following command:<br />

cmdBase::click()<br />

This will execute the parent class code (the Jeepers WAIT WINDOW) in addition to any code<br />

you put in the instance's method. It's just a simple timesaver, but isn't that what I promised earlier<br />

in this article?<br />

Right-clicking on the middle button in the same scenario as the previous example will insert the<br />

following code if you're editing a subclass:<br />

=EVALUATE(this.ParentClass+"::click()")<br />

If you're editing an object member of a container, on the other hand, a right-click will produce<br />

this code:<br />

=EVALUATE(this.Class+"::click()")<br />

If you think that a direct class or parent class might be changed, you might want to use this since<br />

it inserts a generic reference that is evaluated correctly regardless of how the superclass<br />

reference changes.<br />

Finally, the right button on the toolbar brings up the Help file. Now you don't have any excuse<br />

for reading the documentation! And here's a nice touch: Right-clicking on the Help button will


display the version number of SUPERCLASS -- an enhancement we all would do well to<br />

emulate.<br />

Where to find SUPERCLASS<br />

SUPERC.ZIP is public domain software and can be found in download file _SUPERC.EXE as<br />

well as in Library 9 of FoxUser and Library 3 of VFOX.<br />

Whil Hentzen is president of Henzenwerke, a Milwaukee, Wisconsin, software development firm that<br />

specializes in strategic FoxPro-based business applications for Fortune 500 companies. 414-224-7654,<br />

fax 414-224-7650, CompuServe 70651,2270.<br />

Sidebar: Book of the Month<br />

This month's "must have" is the next in a series of three Microsoft Press books on software<br />

development. Writing Solid Code by Steve Maguire (ISBN 1-55615-551-4) is billed as<br />

"Microsoft's Techniques for Developing Bug-Free C Programs," but that shouldn't scare FoxPro<br />

developers. The techniques described in these 250 pages are sometimes specific to C code, such<br />

as "Don't use output memory as workspace buffers" and "Use LINT to catch bugs that your<br />

compiler may miss," and the examples are all in C, but it would be hard to turn two pages in a<br />

row without picking up a valuable idea.<br />

Maguire starts by discussing how bugs come about. He proposes that a programmer ask two<br />

questions about every bug they encounter: "How could I have prevented this bug?", and "How<br />

could I have automatically detected this bug?" He then guides you through assertions, code step<br />

throughs, bug-proof functions, methods to code safer algorithms, risky programming practices,<br />

and finally he covers attitude. "If a programmer regularly `cleans up' code or simply tries<br />

haphazard solutions to problems, hoping to hit upon something that works, writing bug-free code<br />

will be an uphill battle."<br />

In the last chapter he describes a number of real-life events, including the cancellation of an<br />

unannounced Microsoft product due to a runaway bug list, and techniques that could have<br />

prevented those situations. This book is an easy read, but may well be one of the most valuable<br />

books on software development that you can own.<br />

Non-Tabbed Pages: An Alternative to the<br />

Tabbed Interface<br />

Paul Russell (5)


Tabbed interfaces, such as those implemented with Visual FoxPro PageFrames, are useful, but<br />

some users may prefer alternatives. Paul Russell explores an innovative way to implement<br />

multipage forms in FoxPro 2.x for Windows (or Macintosh), and provides a sidebar with tips for<br />

implementing the idea in Visual FoxPro and the character-based 2.x platforms.<br />

By now, most of you have probably become familiar with the tabbed interface style for dialog<br />

boxes. This style was developed for screens that have too much information to display at one<br />

time. For example, the Visual FoxPro Project Manager dialog box has a number of different<br />

"Tabs" across the top, each referring to a specific set of information. This Tab style has become<br />

so standard, in fact, that it has been incorporated as a design tool in Visual FoxPro (see Figure<br />

1).<br />

The FoxPro community is fortunate to have Ken Levy's GENSCRNX with a Tabs driver to<br />

perform the same type of thing in FoxPro for Windows. This tool allows you to have a set of<br />

pages immediately available for a single record, and have all of this information contained in a<br />

single Screen Set.<br />

While this is a useful tool, there are a number of drawbacks. For example, I'm developing an<br />

application with 14 sub-screens that contain information. It would have been impossible to use<br />

the tabbed style and still see what each tab represented (the standard Tabs driver allows only a<br />

single row of tabs that become thinner as more tabs are added). Also, with the standard Tabs<br />

driver, all of the screens are contained within a single Screen Set and all are defined at once. This<br />

displays all of the sub-screens when the screen is first started, which would have confused the<br />

users of this particular application more than it would have helped them.<br />

Requirements of a new style<br />

For this application, using the tabbed style was clearly not the way to go. Instead, I needed a way<br />

to do the following:<br />

• Select any one sub-screen directly, from more than 10 sub-screens.<br />

• Display an obvious visible indicator of which sub-screen was active.<br />

• Be able to select the next/previous sub-screen easily.<br />

• Move directly back to the first sub-screen.<br />

• Avoid displaying all of the sub-screens at the beginning.<br />

• Display text or a bitmap as the prompt for the button.<br />

The solution that I came up with resembles the appearance of the View dialog box in FoxPro for<br />

Windows (see Figure 2). This style displays a set of buttons vertically down the left side of the<br />

sub-screens, and the current sub-screen is indicated by the depressed button. ON KEY labels<br />

were set up so that users could move to the next or previous sub-screen by pressing the PgDn<br />

and PgUp keys. They could move to the first sub-screen by pressing the Home key (while this


isn't strictly a Windows-standard, it's what the customer requested). The main screen and each<br />

sub-screen are stored as separate screen entries in the Project database (that is, not in a single<br />

Screen Set), and a .PRG controls which screens are displayed at any one time.<br />

You can now develop a sample application. All of the source code for this application is<br />

included in download file _RUSSEL2.EXE. Since the purpose of this article is to demonstrate a<br />

screen interface, rather than any database manipulations, this application will work only on<br />

memory variables, which won't be saved when the application exits. Since the data is irrelevant<br />

to the article, you won't perform any validation on the data that gets entered.<br />

The sample application will be a prototype for a customer management system. The main<br />

information (the information that you want to display all of the time) is the customer name and<br />

ID. For this application, you'll have three sub-screens, each with a single piece of information.<br />

The first sub-screen will have the Address, the second will have any Contacts that you've made<br />

with the customer, and the third will be general notes on the customer. For simplicity sake, these<br />

will all be character memory variables.<br />

Define the main screen loop<br />

All of the processing for this screen is controlled from within a single .PRG that calls multiple<br />

.SPR files. The screen files all have the #NOREAD directive and, therefore, can't do anything<br />

more than display the information on the screen. This means that the controlling .PRG contains<br />

the controlling READ. Here's a sample of the controlling loop. In this case, the READ SHOW<br />

and READ WHEN procedures aren't defined; the calls are just for illustrative purposes:


* Initialize the variable for the current screen<br />

m.lnScreen = 1<br />

do while m.lnScreen # 0<br />

* Define the window, draw the header information,<br />

* and draw the buttons for the sub-screens<br />

do scr_main.spr<br />

* Draw the objects for the specific sub-screen<br />

* that you are working with<br />

do case<br />

* Display the Address information<br />

case m.lnScreen = 1<br />

do scr_sub1.spr<br />

* Display the Contacts information<br />

case m.lnScreen = 2<br />

do scr_sub2.spr<br />

* Display the Notes information<br />

case m.lnScreen = 3<br />

do scr_sub3.spr<br />

* More screens would be defined as required<br />

endcase<br />

* Process the READ<br />

READ CYCLE ;<br />

SHOW ReadShow() ;<br />

WHEN ReadWhen()<br />

Setup the navigation through the screens<br />

Now that the main loop is in place, the navigation controls have to be set up to help move<br />

through the sub-screens. This involves creating a procedure (NewScreen) to load the newly<br />

selected sub-screen and setting up the hot keys to navigate through the sub-screens. The<br />

following code is included with the initialization of the program:<br />

* Define total number of screens that you have available<br />

m.lnScreens = 3<br />

* Setup the hot keys to move through the screens<br />

ON KEY LABEL HOME DO NewScreen with 1<br />

ON KEY LABEL PGUP DO NewScreen with m.lnScreen-1<br />

ON KEY LABEL PGDN DO NewScreen with m.lnScreen+1<br />

[You might also consider setting up menu options such as First screen, Next screen, and


Previous screen with the corresponding shortcuts Home, PgUp, and PgDn. This would provide<br />

menu alternatives for the users, teach them about the shortcut keys, avoid the extra code<br />

otherwise needed to make sure the ON KEY LABEL assignments are undone after the user is<br />

finished with the screens, and eliminates the potential for ON KEY LABEL recursion. -- Ed.]<br />

The following code gets placed after the main loop. In this procedure, you'll handle the wrapping<br />

of the sub-screens (pressing PgDn on the last sub-screen moves back to the first one). Switching<br />

the sub-screens is actually handled by issuing a CLEAR READ in the NewScreen procedure.<br />

This is because the main program is in a loop controlled by m.lnScreen. To terminate the loop,<br />

m.lnScreen is set to 0. Any other value will cause the main screen and the correct sub-screen to<br />

be displayed:<br />

procedure NewScreen<br />

*****<br />

* Program : NewScreen<br />

* Programmer : Paul Russell<br />

* Purpose : Move to a new screen<br />

*****<br />

parameter m.lnNewScreen<br />

* Ensure that the parameter is valid<br />

if type("m.lnNewScreen") # "N"<br />

m.lnNewScreen = m.lnScreen<br />

endif<br />

* Loop around the end of the list if necessary<br />

do case<br />

case m.lnNewScreen < 1<br />

m.lnNewScreen = m.lnScreens<br />

case m.lnNewScreen > m.lnScreens<br />

m.lnNewScreen = 1<br />

endcase<br />

* Set the new screen number<br />

m.lnScreen = m.lnNewScreen<br />

* Load the New Screen<br />

CLEAR READ<br />

return<br />

Define the main screen<br />

The screen shown in Figure 3 is the basis for the sample application. The Customer Name and<br />

Customer ID are text fields that you always want to display. The Quit button is provided, in this<br />

case, to allow you to get out of the application (you are usually better off using a menu selection<br />

for this, but that's outside the scope of this article). The large rectangle is the area that will be<br />

filled in with the sub-screen information.


The first key to making everything work is the set of buttons on the left (Page1, Page 2, and Page<br />

3). These are graphical radio buttons. The image in each button is, optimally, 15 pixels high and<br />

a bit wider than it needs to be for the longest label. Radio buttons are used, rather than push<br />

buttons, so that the "pushed in" quality will be retained (if push-buttons were used, they would<br />

only be pushed in for as long as the mouse button was pushed in).<br />

The radio buttons use the variable m.lnScreen, and they call NewScreen as an expression with no<br />

parameters to validate the button. The code in NewScreen then guarantees that the variable will<br />

be correct and that it will be selected automatically. When a different radio button is pushed in,<br />

m.lnScreen will be changed to that value, and the corresponding screen will be displayed.<br />

The second key is changing the information that gets displayed in the screen, so that the old<br />

information is removed and the new information is displayed. This is where the large rectangle<br />

to the lower right comes in. This rectangle is defined as opaque and white with a black hairline<br />

border. It's the same size and position in all of the sub-screens. Objects specific to the sub-screen<br />

are then drawn on top of the rectangle. When a new sub-screen is drawn, the white rectangle will<br />

be drawn first (covering up the old information), and then the new information will be drawn on<br />

top. For this reason, it's often easier to have a "dummy" screen that only has the rectangle<br />

defined and no objects on it. I'll explain how to use this "dummy" screen next.<br />

Define the sub-screens<br />

Once the main screen information has been defined, it's simple to copy the main screen to the<br />

dummy screen and then remove all of the information except for the rectangle. Then you can use<br />

this dummy screen as a basis for the sub-screens.<br />

Copy the dummy sub-screen to each of the sub-screens your application will actually use and fill<br />

in the appropriate field information. Don't forget that the only information in the subscreens that<br />

you fill in is within the white rectangle.<br />

Drawbacks<br />

There are drawbacks with everything, and this style is no exception. Fortunately, you can<br />

overcome most of them with a bit of imagination.<br />

One major drawback is that the radio buttons require that images be created for them. This is to<br />

ensure that they are graphical buttons and look like regular push buttons and not like standard<br />

textual radio buttons. You can create an image using the Paintbrush program that comes with<br />

Windows (in the Accessories Group) or the Paint program that comes with Windows 95. One<br />

difference between this style and the tabbed dialog style is that this style requires more planning<br />

because these bitmaps can't be changed at runtime (while it is possible to use FoxPro to turn text<br />

into a properly formatted bitmap, I haven't written the procedure for this yet). However, this does<br />

turn into an advantage because you can incorporate the graphical and textual elements in a single<br />

place, and you'll immediately see how they look.<br />

Another major drawback is that you can't display any @ .. SAY objects on the screen and expect<br />

them to be refreshed. This is because the READ is outside the screen program (.SPR), and there's


no automatic way for it to reference the SHOW routine in the .SPR. For more information on<br />

this, and for potential workarounds, refer to the sidebar, "Refreshimg @ ..SAY Objects."<br />

The line of bitmaps contained within the radio button definition can get prohibitively long. In the<br />

first application that I used this style for, the list of the 14 bitmaps that GenScrn created,<br />

complete with the DOS PATH to find them, exceeded the FoxPro command-line limitation of<br />

2,048 characters. To get around this, I used the GENSCRNX directive *:PICTURE in the<br />

comment snippet of the radio button. I was then able to set up the list of buttons in a character<br />

string at the beginning of the program. This also turns into an advantage, as you'll discover later.<br />

The bulk of this style has to be coded by hand. There is no GENSCRNX driver for it and, with<br />

the release of Visual FoxPro, this driver probably will never be developed.<br />

The rectangles on the separate sub-screens have to be defined in advance and are included in<br />

each sub-screen for reference when placing the objects and to hide the previously drawn objects.<br />

If the space required for the objects in a sub-screen exceeds the size of the rectangle, then the<br />

rectangle has to be expanded to the same size in all of the sub-screens. This is easy to overlook<br />

during development, but it's easy to check by building and running the application, then moving<br />

through all of the sub-screens, one after another.<br />

Possible extensions to this style<br />

While developing an application, I wanted to change the order of the radio buttons and the<br />

corresponding sub-screens frequently (due to chasing loose requirements from the users and their<br />

managers). For this reason, I changed the initialization of the program to enumerate the<br />

sub-screens and then build the list of buttons on the fly -- when I wanted to change the order of<br />

the sub-screens, I would just change the value in the enumerated buttons, and the program would<br />

re-build the list accordingly.<br />

In this article, I've shown a basic sample of this style. In one of my applications, I've used this<br />

style to contain 20 sub-screens of information, with 10 buttons being displayed at one time. This<br />

was possible because I included two extra radio buttons arranged horizontally at the bottom of<br />

the list (see Figure 4). When one of these buttons is selected, the list of sub-screen bitmaps is<br />

changed (because of the GENSCRNX *:PICTURE directive I mentioned earlier) and a CLEAR<br />

READ is issued. When the program goes through the loop again, it re-draws the radio buttons,<br />

this time with the new bitmaps. The value of the sub-screen is also increased or decreased by 10.<br />

Using this extension, the number of possible sub-screens can easily be tripled or quadrupled (and<br />

if a pull-down is used instead, there can be an almost unlimited list of sub-screens).<br />

The user of one application wanted a more distinct way to see which button was depressed. We<br />

decided that a box could be drawn around the text for the depressed button. Since the<br />

*:PICTURE directive was already being used, it was a trivial matter to let FoxPro handle this<br />

change for us.<br />

FoxPro has a feature (see "Liven Up your Applications with 3D Graphical/Text Push Buttons" in<br />

this issue) that allows you to set up three different images for each graphical button. The first is<br />

the button in the normal state, the second is the button in the depressed state, and the third is


when the button is disabled. The Screen Builder allows only for the first of these, so you have to<br />

get away from the limitations of the Screen Builder (in this case, it's done by using GENSCRNX<br />

and the *:PICTURE directive again).<br />

The code to define the list of bitmaps for this applications sets up two bitmaps for each button. In<br />

this case, est_gi_p.bmp is identical to est_gi.bmp, with the exception that it has a single-line<br />

border around the outside of the button. As different buttons are pressed, the border moves<br />

automatically (see Figure 5):<br />

m.lcPicture = "est_gi.bmp,est_gi_p.bmp;"+ ;<br />

"est_je.bmp,est_je_p.bmp"<br />

My users have become accustomed to the buttons and like being able to go where they want and<br />

see what they want so much that they found the menu system to be the long way to get things<br />

done. After filling in a complete order, users had to print three reports, and they were looking for<br />

a faster way than to select the three reports individually from the menu. A push button was added<br />

below the list of radio buttons. This push button had the same dimensions and almost exactly the<br />

same appearance as the radio buttons, so the users felt instantly comfortable with it. But the label<br />

on the button was "Print All." This button replaced the selection of three menu items so that the<br />

users could quickly print the three reports with the single order.<br />

Conclusion<br />

A wide range, both of tools and of programming and interface styles, are available in the<br />

development community. I see this daily in the hundreds of e-mail messages that I get over the<br />

Internet. Developers always want to include the best in their projects. It's good that developers<br />

are looking for alternatives because, when you see one, you can make the decision to use it right<br />

away, never use it, use it but adjust it a bit, or remember that you saw it and try it later.<br />

I hope that showing you this alternative prompts you to expand your tool set and join in the<br />

at-work and online discussions about other ideas, as well.<br />

Paul Russell is a software specialist with Software Kinetics Ltd., is chairman of the Fox Users of Nova<br />

Scotia, and is an active member of the FoxPro sections on the Internet. He has been programming for<br />

more than 10 years, with more than seven of those years of experience with the Fox product line. Internet<br />

prussell@fox.nstn.ca, or russell@atl.sofkin.ca, CompuServe 102440,2125.<br />

Sidebar: Refreshing @ .. SAY objects<br />

When a screen is generated, an extra procedure is created to handle the READ SHOW clause.<br />

All of the @ .. SAY (or "Output") objects that have the [ ] Refresh box checked are included<br />

twice -- once in the main definition of the screen, and again in the READ SHOW clause. Later,<br />

when the SHOW GETS command is executed, the READ SHOW procedure gets called. Using


this mechanism, FoxPro takes care of re-drawing the @ .. SAY objects. All you have to do is<br />

issue the SHOW GETS command when you want them refreshed.<br />

There are two problems with this style. The first is that the READ SHOW procedure is in a<br />

different file and has a random name (generated by GenScrn), and the second is that if the screen<br />

is generated with the #NOREAD clause, then no READ is generated (as expected), but also no<br />

other clause that is directly connected with the READ is generated. This effectively means that<br />

no READ SHOW, READ WHEN, or other such clauses are generated. These two problems<br />

mean that you can't issue a standard READ SHOW command and have the @ .. SAY objects<br />

refresh normally.<br />

Getting past the first problem is relatively easy. It involves two steps: telling the READ SHOW<br />

procedure what its name should be, and then calling it explicitly. Since the most common use of<br />

the READ SHOW clause is to refresh the @ .. SAY objects, the following code could be inserted<br />

into the SHOW snippet of all of the screens, including the main screen, to explicitly name the<br />

READ SHOW clause:<br />

#NAME SHOWSAYS<br />

Then, in the ReadShow procedure, you can call the correct procedure in all of the relevant<br />

screens (you'll notice that this code is remarkably similar to the main loop):<br />

do SHOWSAYS in scr_main.spr<br />

* Refresh the @ .. SAY objects in all of the sub-screens<br />

do case<br />

case m.lnScreen = 1<br />

do SHOWSAYS in scr_sub1.spr<br />

case m.lnScreen = 2<br />

do SHOWSAYS in scr_sub2.spr<br />

case m.lnScreen = 3<br />

do SHOWSAYS in scr_sub3.spr<br />

* More screens would be defined as required<br />

endcase<br />

Getting past the second problem, not creating the READ SHOW procedure at all, is a bit more<br />

difficult. In this case, you have to replace the #NOREAD directive with a call to a GENSCRNX<br />

driver. This driver is used to post-process the generated screen and remove the READ command.<br />

In the Setup Snippet of all of the screens, include the following line:


*:SPRDRV6 NO_READ<br />

Here's the GENSCRNX driver, NO_READ.PRG (it must be located anywhere in the FoxPro<br />

PATH):<br />

* Driver to remove the READ commands<br />

* This is better than using #NOREAD because it retains<br />

* the supporting procedures, like the SHOW snippet.<br />

* This is best called as *:SPRDRV6<br />

replace sprdata.spr with ;<br />

strtran(sprdata.spr,chr(13)+chr(10)+"READ ",;<br />

chr(13)+chr(10)+"* READ ")<br />

replace sprdata.spr with ;<br />

strtran(sprdata.spr,chr(9)+"READ ",chr(9);<br />

+"* READ ")<br />

This driver looks for the READ command at the beginning of the line and replaces it with a<br />

commented READ command. Two REPLACE commands are included: the first for a<br />

single-platform screen and the second for a multiplatform screen.<br />

Without including both changes (the #NAME directive and the NO_READ driver), it's<br />

impossible to include @ .. SAY objects in the screens and have them be refreshed. Instead, you<br />

have to have all refresh-able objects as @ .. GETs.<br />

Sidebar: Non-tabbed pages<br />

Moving this style to FoxPro for DOS is also fairly easy, but there are some limitations because<br />

of the nature of the character environment. When moving this style to DOS or UNIX, the radio<br />

buttons along the left side of the sub-screens are changed to regular push-buttons. No other<br />

changes need to be made to the basic idea. The functionality that gets lost is the ability to have a<br />

bitmap (and therefore a graphic) represent the button, and there's no automatic way to change the<br />

prompt on the button for the new sub-screen (it's possible, just not automatic). You also lose the<br />

ability to have multiple "pages" of buttons, because there isn't enough room in most lists of<br />

buttons to have the buttons.<br />

One technique you might consider in a character-based environment to compensate for the lack<br />

of visual clues such as the "always pressed" status of a graphical radio button would be to add<br />

code to the NewScreen procedure that enables all the page push buttons, then disables the one<br />

for the new page.<br />

Moving this style to Visual FoxPro is easy and will work more smoothly. The sub-screens can be<br />

defined as a tabbed interface, but without the tabs. The radio buttons can appear along the left<br />

edge of the tabbed form, and the Valid event on the radio buttons would behave the same, setting


the new page as current and then issuing a THISFORM.Refresh call.<br />

New Year's Resolutions<br />

Les Pinter<br />

I'm making up my list of resolutions for the new year. I know it's early, but one of my New<br />

Year's resolutions last year was not to get behind . . .<br />

I will learn to live with Windows 95. I tried it for two weeks, then reformatted my hard drive and<br />

went back to NT 3.5. But most if not all of my clients will use it, so I will do the same.<br />

I will register all of my shareware. I use WinZip every day, and it's only $30 or so. And<br />

McAfee's Antivirus has saved my bacon more than once. I'll even buy the terrific PIM I've been<br />

"test-driving" since a friend gave it to me six months ago. I know you'll all ECCO my<br />

sentiments.<br />

I will read every single article in both <strong>FoxTalk</strong> and that journal that won't sell me advertising. A<br />

single idea from a single article that I read a month ago is saving me a half an hour a day. So I'll<br />

pencil reading time into my schedule.<br />

I will check out the collection of applications being developed by the<br />

Foxfire!/Flash/FoxExpress/Capcon brain trust. You can bet your boots that this collection of the<br />

best minds in our business, working together, will produce something worth looking into.<br />

I will get on the Internet. Even though I'm told it's a big waste of time, there's got to be<br />

something to any technology that doubled its user base during the last year. So I'll learn what a<br />

gopher is and surf the web.<br />

I will make my deadlines to <strong>FoxTalk</strong>. Honest, Bob . . .<br />

I will give my Madrid DevCon speech in Spanish, and my Moscow DevCon speech in Russian,<br />

even if my partner Dmitry has to cover his ears and leave the room.<br />

I will check my e-mail every day. Mainly because it's in my CompuServe Forum contract. GO<br />

PINTER . . .<br />

I will read every book on Visual FoxPro that comes out. So far, Whil Hentzen's is the best Visual<br />

FoxPro book, and after mine comes out in December (McGraw/Hill), Whil's will still be the best.<br />

But there's something to be learned from every title.<br />

Most of all, I will appreciate the wonderful job that I have. I earn a good living, and get to do<br />

what I love most. In a way, it's just like high school. All you have to do is do your homework<br />

and show up.<br />

Les Pinter publishes the Pinter FoxPro Letter in the United States and Russia. 916-582-0595.<br />

CompuServe 76434,104.


Fox World News<br />

Micromega Systems has announced a new version of their FoxFire! Report Writer. Version 3.0<br />

supports FoxPro for DOS, FoxPro for the Macintosh, FoxPro for Windows, and Visual FoxPro<br />

and is fully customizable without changing source code. Other features include outer-join<br />

support, top N values, and a data-on-file window for validating values. In addition, the cross-tab<br />

reports have separate cross-footings and easier sorts. You can create spreadsheets or Excel pivot<br />

tables via DDE, graphs with MS-Graph or Excel, and World Wide Web browsable reports.<br />

Micromega Systems Inc., 832 Baker St., San Francisco, CA 94115, 800-453-6655,<br />

415-346-1461, fax 415-346-6804, CompuServe 73354,1745. Single user standalone edition $99,<br />

developer's edition $295.<br />

Strategic Edge is shipping Query Maker, the distributable RQBE and report writer, for Visual<br />

FoxPro. In addition to a new look and feel, this version features a new form layout report option,<br />

a detail/summary report option, a report-column field, and filter builder improvements. In<br />

addition, four more output options have been added: Visual FoxPro .DBF tables, ASCII text file<br />

with tab delimited fields, ASCII text files with blank delimited fields, and Excel version 5.0<br />

spreadsheets. Strategic Edge, 415-563-3755, fax 415-563-1907, CompuServe 74660,1422.<br />

Application Developers is offering introductory, intermediate, and advanced classes in Visual<br />

FoxPro 3.0. Introduction to Programming in Visual FoxPro is a two-day course that includes a<br />

tour of the software, database design, importing data, designing forms, designing queries, and<br />

designing reports. The intermediate Programming in Visual FoxPro is a two-day course that<br />

includes developing maintainable apps, database design, the data dictionary, GUI development<br />

principles, object-oriented programming, advanced form design, designing menus, distributing<br />

apps, and using SQL in Visual FoxPro. Advanced Programming in Visual FoxPro is a one-day<br />

course that includes advanced multiuser programming, advanced database concepts, advanced<br />

class design and implementation, understanding OLE, and extending the development<br />

environment. The classes are held in major cities across the United States and Canada.<br />

Application Developers Training Company, 7151 Metro Blvd, Ste. 175, Minneapolis, MN<br />

55439, 800-578-2062, 612-943-1363, fax 612-942-8452. Introductory and intermediate courses<br />

$795. Advanced course $395.<br />

Extended Systems has announced its Advantage Database Server version 4.0, previously<br />

known as Advantage Xbase Server. The new version adds new server functionality, such as<br />

transaction processing, new data storage capabilities, new data security features, and Windows<br />

support through two new interfaces. Extended Systems has teamed with SuccessWare and<br />

integrated the SuccessWare Database Engine version 2.0 (SDE2) interface into Advantage<br />

Database Server. This gives you a navigational Windows .DLL interface. You can create<br />

scalable .DBF applications. Advantage 4.0 also provides support for SQL data access through its<br />

Level 2 ODBC interface. Extended Systems, PO Box 6368, Bozeman, MT 59771,<br />

800-235-7576, 208-322-7800, fax 208-377-1906.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!