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.

March 1996<br />

Extend the Report Writer with GenRepoX<br />

2.0<br />

Markus Egger (1)<br />

Just when you thought you were done with tools like GENSCRNX and GENMENUX, along comes<br />

a similar public domain utility from Austria that does for reports what the first two add-ons did for<br />

screens and menus. GenRepoX performs many of the same types of functions, but also performs<br />

some report-specific actions and is controlled in a somewhat different fashion due to FoxPro's<br />

non-compilable .FRX structure. The bottom line: easily enhanced report functionality that should<br />

be in every developer's toolkit.<br />

GenRepoX is a power tool extender similar to GENSCRNX and GenMenuX. (It extends the<br />

Form/Class Designer, the Menu Designer and, last but not least, the Report Designer.) In FoxPro<br />

2.x, screens were generated, and as a result, GenScrnX was one of the most important tools for<br />

every developer. In Visual FoxPro 3.0 it's no longer necessary because there is no more source<br />

generation. We have the same situation with the report writer because reports have been printed<br />

directly from the .FRX file since reports were introduced to FoxPro. This means that GenRepoX<br />

has to approach the power tool enhancement process differently. GenRepoX does all its changes<br />

on-the-fly. GenRepoX copies your .FRX file to a temporary one, makes the changes in this<br />

temporary copy, and prints the report from the copy. This might sound like it would be slow, but<br />

since .FRX files are .DBF tables with a different file extension, GenRepoX can take advantage<br />

of FoxPro's natural table-oriented speed.<br />

If you use GenRepoX, you can use *:-commands in your comment snippets, just like the other<br />

GENX commands. (There are additional places where you can use these commands, but I'll<br />

cover this later.) For example, you could place the command *:UNDERLINED in a field to<br />

create an underlined font, which is very useful since this isn't possible in FoxPro 2.x. GenRepoX<br />

works in both Visual FoxPro 3.0 and in FoxPro 2.x. Since it doesn't rely on Visual FoxPro<br />

features, even features like object orientation and events work in FoxPro 2.x.<br />

Using GenRepoX<br />

GenRepoX is a .PRG program. There's no magic involved. Since there's no generation process in<br />

reports, FoxPro doesn't have a system variable (like _GENSCRN) to define which program is<br />

used to print the report. This means you have to tell FoxPro directly that you want to use<br />

GenRepoX. Normally, you would call a report like this:<br />

REPORT FORM Test TO PRINTER


If you want to use GenRepoX, you have to call it, and pass the original command as a parameter:<br />

=GENREPOX( "REPORT FORM Test TO PRINTER" )<br />

As you can see, the original command is passed as the parameter, and it is used to print the<br />

report. This means that GenRepoX is 100 percent compatible with the normal FoxPro report<br />

writer. GenRepoX just takes the command, looks for the report name, makes a copy of this<br />

report, and makes some changes. Normally, the original report isn't changed at all (there's only<br />

one case, functions, which I'll discuss later). Finally, GenRepoX uses the original command to<br />

print the report.<br />

This also means that you have to include GENREPOX.PRG in your project. This isn't a problem<br />

because GenRepoX is a public domain tool, and you can copy it and ship it with your products<br />

as much as you want.<br />

Until now, you've seen that you can pass a single parameter to GenRepoX, identifying the report<br />

to run. But there's more. You can pass three parameters like so:<br />

=GENREPOX(Command, DoRun, CommentSnippet)<br />

The parameters are as follows:<br />

• Command -- This is the normal report command that you've used so far.<br />

• DoRun -- This parameter defines if the report should be printed, or if you just want to do<br />

a generation run. This is useful when you want to make sure that the report was created<br />

properly and all the functions are up-to-date.<br />

• CommentSnippet -- This is only for compatibility to version 1.x.<br />

Some GenRepoX commands have to be used in the global comment snippet of the report.<br />

Unfortunately, you can't edit this snippet (unless you open the .FRX directly with a<br />

USE-command). For this reason, you can pass the whole global comment snippet as a parameter.<br />

But make sure to separate every command with a CHR(13).<br />

You don't need this parameter in version 2.0 since you can pass all this information in the first<br />

parameter. To keep the compatibility in the original command, GenRepoX cuts all this additional<br />

information off before the report is printed.<br />

A simple example<br />

To create a simple report with one field, add the GenRepoX command "*:UNDERLINED" in the<br />

comment snippet of the field. Now print the report as usual with the REPORT FORM command.<br />

As you see, the report prints as usual without any underlined fonts, but there are no problems due<br />

to the contents of the comment snippet. Now call GenRepoX and pass the command as a


parameter. The first thing you see is a wait window saying "Running GenRepoX..." This is a hint<br />

for the programmer that everything is working fine. This window appears only in the<br />

development environment. When you look at the report, you'll see that the field was printed in an<br />

underlined font. Voilà: that's magic, right?<br />

Well, almost. In fact, GenRepoX just changed some fields in its copy of the FRX, which enabled<br />

some functionality that the report writer hides because there is no interface for it. But GenRepoX<br />

does more. I'm sure most of you have called a function in your reports instead of using a simple<br />

field or variable name. This function was stored in a procedure file or somewhere in your calling<br />

stack. This worked fine until you gave the report to your friend and you forgot the function.<br />

In fashionable coding, we try to create "black boxes." These are functions/objects/units that work<br />

without knowing anything about the world outside. Well, these reports weren't black boxes at all.<br />

For this reason I tried to bundle the functions to the report, like we do with objects today. So<br />

with GenRepoX, you'll code the whole function directly into the comment snippet of each item<br />

using the *:FUNCTION command. GenRepoX compiles this functions and stores the compiled<br />

version in the report. This is the only case where GenRepoX changes the original report. But<br />

don't panic, it doesn't affect anything. This technique is very similar to what Visual FoxPro does<br />

in forms/classes and the database container. These functions can't be changed in the .EXE<br />

version. Changes are just ignored since FoxPro can't compile functions in the runtime version.<br />

Other features<br />

Version 1.1, built back in the days of FoxPro 2.x, was a giant leap from the old way of<br />

generating reports. It had a bunch of really useful features, like coding functions into reports.<br />

You were also able to color fields and change their font depending on their value. You were able<br />

to create underlined fonts, and you were able to use all possible colors, not only the 16 that were<br />

supported by the report writer. There was a feature that allowed the user to choose which fields<br />

should be printed without anything more then one command. And there was a detail extender<br />

that allowed you to print multible detail bands (for example, in group footers). And there were a<br />

bunch of other little features (see Table 1 and Table 2 following this article). Most of these<br />

features didn't just enable hidden features, but they added real functionality to the report writer.<br />

But then, Visual FoxPro came along with a lot of new features: object orientation, database<br />

container, a new event model, and what about reports? It was time for GenRepoX to grow up and<br />

to do what the developers at Microsoft didn't have time to do.<br />

Object-oriented reports<br />

The most important feature of version 2.0 is object orientation. GenRepoX has it's own object<br />

model, which is similar to the object model of Visual FoxPro. But it's a completely independent<br />

model in that it handles some things that are not possible in VFP.<br />

Let's start with the basics. Those of you who used GenScrnX might know the library concept that<br />

Ken Levy used in GENSCRNX, and they might think "that's an old story. We're not gonna read<br />

this again." But hey, stop! I'm talking about something completely different here.


GENSCRNX's library model takes a record and copies it over an existing one. GenRepoX<br />

actually has a real object model. Every object in a report has properties and methods, and you<br />

can define classes and subclass them. You can also subclass a subclass, and so on. You don't<br />

have a limitation of how deep subclasses are nested. A subclass inherits everything that's defined<br />

in the ParentClass, except the things that have been overwritten. For example, if your<br />

ParentClass has a PrintWhen Condition, all the subclasses get the same PrintWhen Condition.<br />

But if you write something into the PrintWhen snippet of your subclass, you override the<br />

PrintWhen Condition of the ParentClass. However, just as with Visual FoxPro, there are ways to<br />

write something in a subclass's method and still access the parent class's contents as well. For<br />

example, you could put something in the PrintWhen Snippet and get the PrintWhen Snippet of<br />

the ParentClass as well. As with Visual FoxPro, you can do this with the "*::" operator.<br />

However, don't mistake this operator as being identical to Visual FoxPro's "::" operator. You can<br />

use it to reference something in a ParentClass. The difference is that in Visual FoxPro you can<br />

only use it in Methods. In GenRepoX you can use it in properties as well.<br />

Another difference to note is that you can't leave some parameters empty. This means, that you<br />

override the properties in the ParentClass by default. You have this problem, for example, with<br />

the Expression and with the Fonts. There is some added GenRepoX functionality<br />

(CLEAR_EXPRESSION, CLEAR_FONTINFO, CLEAR-FONTSTYLE, and so forth) that<br />

deletes this stuff and the inheritance works fine then.<br />

You define a class using the "*:CLASS ClassName" command. Then the object automatically<br />

becomes a class, and the report where it is stored becomes it's ClassLib. But you can still print<br />

this ClassLib like a normal report. The *:CLASS-command doesn't have a meaning then. To<br />

create a subclass, just add "*:AS_CLASS ClassName OF ClassLib.FRX" to another object/class,<br />

and it inherits everything from the defined class. And as I already said: You can have multible<br />

levels of inheritance and the chain can become as long as you want. You don't really need to say<br />

"...OF ClassLib.FRX". You can also define standard classlibs.<br />

One big feature is multiple inheritance. This means, that a class can have more than one<br />

ParentClass. For example you have a special class for PrintWhen Conditions. You have another<br />

class with some special stuff. You can now create a class that subclasses from the PrintWhen<br />

Class and from the special class as well. This is a very powerful feature. But sometimes it's<br />

difficult to handle because you don't know what the real ParentClass is. Anyway, I give you this<br />

functionality. If you like it, use it and enjoy it; if you don't like it or if you are uncomfortable<br />

with it, don't use it. Creating a class that inherits from two or more (again no limits) other classes<br />

is simple. Just use more then one *:AS_CLASS - statement.<br />

The first class from which you inherit has the highest priority and is the leading class. For<br />

example, if the first class writes something into the PrintWhen snippet, the second class can't<br />

overwrite this anymore. (Exception: If you use the *::-operator, the functionality in the second<br />

class is added to the existing functionality.)<br />

As you can already see, GenRepoX's object-model supports a lot of things that even VFP itself<br />

can't do. But in version 2.0 there is another cool feature besides object orientation: Events.


Events<br />

If you explore the report writer, you can find events in reports, but all these events are related to<br />

the data environment, so they aren't really part of the functionality of the report. But wouldn't it<br />

be cool to have some events like GotFocus right before the object is printed, or LostFocus right<br />

afterwards, or maybe an Error-Event to trap errors while the actual object is printed? Well, with<br />

GenRepoX you have those Events.<br />

Create a simple report with one field and write the following code into the comment snippet:<br />

*:EVENT GotFocus<br />

GO 999999999<br />

*:ENDEVENT<br />

*:EVENT Error<br />

ACTIVATE SCREEN<br />

? MESSAGE()<br />

*:ENDEVENT<br />

In this example, you move the record pointer to record number 999999999 right before the field<br />

is printed. This causes an error (unless your table has that many records), and this error is<br />

handled by the error event, which can be thought of as the object's private little error handler. In<br />

this error handler, you activate the screen and display the error message. Then you continue<br />

printing the object. Have you seen more intelligent error handlers than this one? Well, you can<br />

use this event for whatever you want. Think about the error handlers you could build:<br />

The driver-model of GenRepoX<br />

Since version 1.1, a driver model is included, which is very similar to the one in GenScrnX.<br />

There are several driver hooks available. A demo-driver is included (CHECKBOX.PRG). You<br />

can install this driver with the following line in your CONFIG.FP(W) (or you can pass it as an<br />

additional statement in the command-line):<br />

_GRXDRV6="CHECKBOX.PRG"<br />

This driver creates graphical check boxes in reports. You may use this driver on logical fields.<br />

To create a check box instead of the usual logical field, add the following line to your<br />

comment-snippet:<br />

*:CHECKBOX The Text you want<br />

The report shows a hooked check box if .T. If you want a crossed check box instead of a hooked


one, add the keyword CROSS:<br />

*:CHECKBOX The Text you want CROSS<br />

For version 2.0, there are several drivers that, if they aren't available as you are reading this,<br />

should be published shortly. There is an HTML-driver that can print in HTML format<br />

(Internet-WWW-Page-format). There will also be a driver that can print directly to an e-mail<br />

address. In fact, there will be several drivers: one will send the e-mail to MS Mail/ MS Exchange<br />

and another one will directly logon to CompuServe, Microsoft Network, and the Internet.<br />

Someone could even create a driver that can communicate with Lotus cc:Mail. I''ve also thought<br />

about a driver that prints in .RTF-format, and Steven Black is considering a driver for his INTL<br />

Toolkit.<br />

Some of those drivers are commercial products and not Public Domain like GenRepoX. If you<br />

find a driver somewhere, please read the license agreement to figure out if you are allowed to use<br />

the driver for free, or not.<br />

Where to get GenRepoX?<br />

GenRepoX is public domain and the latest version is included in download file _EGGER.EXE.<br />

You can download it from all the FoxPro forums on CompuServe, from the Microsoft Network,<br />

and probably in several locations on the Internet. New versions are uploaded frequently, so<br />

check often.<br />

If you don't have access to online services, ask your user group if they can put it in their libraries.<br />

There are also some commercial products, like Foxfire!, which ship including GenRepoX.<br />

Markus Egger has been involved in the computer business for more then 10 years. He owns the European<br />

software house EPS-Software and is involved in the American company MTI-Software. He works with<br />

Visual FoxPro and Visual C++. He is an international speaker and author. His best-known product is the<br />

public-domain tool GenRepoX. CompuServe 100337,1062.<br />

Table 1. The .FRX Structure. If you want to create drivers for GenRepoX, you need to<br />

know how the .FRX files are organized. Therefore I documented the most important things<br />

about the .FRX structure. The following table documents only the most important things.<br />

If you have a special question, feel free to send me an e-mail.<br />

Fieldname Description<br />

PLATFORM Platform for which this record will be used.<br />

UNIQUEID That's a unique ID for every record. It doesn't havea special meaning. Just<br />

make sure that this ID is really unique, if youwant to add records on your own.


TIMESTAMP On this field you can see when the record was created.But it doesn't have any<br />

special additional meaning.<br />

OBJTYPE The first really interesting field. It contains informationabout the kind of stored<br />

information. Here is a short description of thepossible values:<br />

1 = Definition-record. You can find this record in every report.<br />

5 = Label<br />

6 = Line<br />

7 = Rectangular/ Shape<br />

8 = Datafield<br />

9 = This record contains information about a band, like the<br />

detail-band,headings, footer, and so forth<br />

17 = Image or OLE-Object<br />

18 = Report variable<br />

23 = Default values for the report, like Fontshape and size.<br />

25 = That's the data environment (VFP only)<br />

26 = That's an object (cursor or relation) in the data environment. (VFPonly)<br />

OBJCODE The meaning of this field depends on the value inOBJTYPE. Mostly, this<br />

values are in combination with OBJTYPE = 4. In thiscase the values have the<br />

following meaning:<br />

0 = Report title<br />

1 = Pageheader<br />

3 = Groupheader<br />

4 = Detail<br />

5 = Groupfooter<br />

7 = Pagefooter<br />

8 = Summary<br />

NAME The information in this field depends on the actualobject as well. If the actual<br />

record describes an Image or an OLE-object,this field contains the filename or<br />

fieldname. If the actual record describesthe data environment (VFP only), the<br />

value in this field could be "DataEnvironment,""Cursor," or "Relation."<br />

EXPR Also this field depends on the ObjectType. It storesthe fieldname of a datafield,<br />

the displayed text of a label, or the propertiesof an object in the data<br />

environment.<br />

VPOS This field stores information about the vertical positionof the actual object. It's<br />

important to know that this position is the absoluteobject position as it appears<br />

on your screen. This means that the heightof each band counts as well, even if<br />

this band is just a graphical helpin design time. I'll give you a little example: If<br />

we have a report, whichhas a Pageheader with a height of 0 (that means<br />

completely on top) and afield about 1 inch below the pageheader, the stored<br />

position isn't 1 inch;it is 1 inch plus the graphical height of the band.<br />

The stored values are one-thousandth of a cm.<br />

HPOS The horizontal position of the object.


HEIGHT The height of the object.<br />

WIDTH The width of the object.<br />

PICTURE This field contains the style-information of a field.This information is<br />

compatible to the picture command of FoxPro.<br />

COMMENT This is the comment snippet.<br />

ENVIRON This field defines if you want to store the DataEnvironment.(FoxPro 2.x only)<br />

FILLCHAR This field contains information about the fieldtypeof the actual object. For<br />

example "C" for Chars, or "N"for numeric, and so forth.<br />

TAG This field contains different data depending onthe actual objectype. In the first<br />

record of each report, this field containsbinary information about the used<br />

printer driver. This information is compatibleto the C structure you need to<br />

pass to the printer driver if you want toprint in C.In all other records there is<br />

written informationabout the actual object. For example "PHEAD" if the actual<br />

recordis a pageheader. If you clear this field in the first record, FoxPro<br />

forgetsabout the used printer driver and looks for the actual printer. This<br />

makesyour reports more generic.<br />

TAG2 In this field you have binary information about theprinter driver as well. If you<br />

clear this field in the first record, FoxProforgets about the used printer driver<br />

and looks for the actual printer.This makes your reports more generic.<br />

PENRED, PENGREEN,<br />

PENBLUE<br />

FILLRED, FILLGREEN,<br />

FILLBLUE<br />

In this field FoxPro stores information about theforecolor of the actual object.<br />

These are standard rgb-values. In FoxPro2.x, the dialog only allows 16 colors,<br />

but you can define all possible valuesin this field, and FoxPro (2.x and 3.0) is<br />

able to print all of these values.<br />

These are the values for the backcolor.<br />

PENSIZE This defines the pensize.<br />

PENPAT The line pattern.<br />

FILLPAT The fill pattern.<br />

FONTFACE The font name of the actual object. Note: Use only TrueType-fonts in your<br />

reports.All other fonts just ask for problems. And as Murphy want's it, these<br />

problemsalways occur when the first user tries to print. If you create reports<br />

onlyfor yourself, or for a special user, you can also use printer fonts.


FONTSTYLE This field contains codes for fontstyles:<br />

0 = Normal<br />

1 = Bold<br />

2 = Italic<br />

3 = Bold and Italic<br />

4 = Underlined<br />

5 = Bold and underlined<br />

6 = Italic and underlined<br />

7 = Bold, Italic and Underlined<br />

Note: In FoxPro 2.x you can't activate underlined attributes. However, theyare<br />

already supported if you enter the underlined codes directly into the.FRX.<br />

FONTSIZE The fontsize.<br />

MODE This field defines if the actual object is transparentor not.<br />

1= Transparent<br />

2 = Opaque<br />

In FoxPro 2.6 there are some problems with this field. Sometimes FoxProjust<br />

prints black blocks instead of fields. In this case, this field isset to 2. Just fix<br />

this problem manually.<br />

RULER, RULERLINES, GRID,<br />

GRIDV, GRIDH<br />

FLOAT, STRETCH,<br />

STRETCHTOP, TOP, BOTTOM<br />

In these fields, FoxPro stores information about theruler and the grid. This<br />

information is only important at design time anddoesn't have a meaning for the<br />

final printing.<br />

These fields define if the actual object floats inthe actual band if the band<br />

grows (FLOAT = .T.), if the field should stretchif the data gets too long<br />

(STRETCH = .T.), if the field should stretch,but keep it's position<br />

(STRETCHTOP = .T.) and if the field is positionedrelative to the top of the<br />

band (TOP = .T.) or to the bottom (BOTTOM = .T.).<br />

NOREPEAT If this field is set to .T., the field is only printedonce.<br />

RESETRPT If this field is set to .T., the report will be resetafter printing this object.<br />

PAGEBREAK .T. means that there will be a pagebreak after printingthis object.<br />

COLBREAK .T. means that there will be a column break afterprinting this object.<br />

RESETPAGE This field defines if there should be a page reset(_PAGENO = 1) after printing<br />

this object.<br />

GENERAL This field defines if and how a graphic stretches:<br />

0 = Cut Image<br />

1 = Stretch proportional<br />

2 = Stretch and fill the frame


SPACING Defines the line space in textfields:<br />

0 = normal space (1 line)<br />

1 = 1 _ lines<br />

2 = 2 lines<br />

DOUBLE Should a general-field be printed centered (.T. =Yes, .F. = No)<br />

SWAPHEADER If this field is .T., the header is printed on a newpage.<br />

SWAPFOOTER If this field is .T., the footer is printed on a newpage.<br />

EJECTBEFOR This field defines if there is an eject before thereport is printed.<br />

EJECTAFTER This field defines if there is an eject after thereport is printed.<br />

PLAIN This field defines if the header will be printed onlyon page one or nor (DOS<br />

and UNIX only).<br />

SUMMARY This field is used in DOS and UNIX and defines thekind of summaries.<br />

ADDALIAS This field defines if there should be an alias addedto the fieldnames, or not.<br />

OFFSET This field is used for a lot of different functions.In DOS and UNIX it is used to<br />

store report and band information. The usageof this field depends on the actual<br />

objecttype:<br />

Images: In this case, this field defines the kind of source (bitmap or general).<br />

Labels: This field is used to store the alignment (I'm not talking aboutfields!).<br />

Shapes: This field is used to store the curvature.<br />

Lines: Is the line horizontal or vertical?<br />

TOPMARGIN This field is used to store the top margin of thereport.<br />

BOTMARGIN This field is used to store the bottom margin (DOSand UNIX).<br />

TOTALTYPE This field contains coded information about the kindof calculation:<br />

0 = Nothing<br />

1 = Count<br />

2 = Sum<br />

3 = Average<br />

4 = Lowest<br />

5 = Highest<br />

6 = Std. Deviation<br />

7 = Varianz


RESETTOTAL This field defines when the calculation should bereset.<br />

CURPOS If this field set to .T. (in the first record), thecursor position is displayed in the<br />

status bar (Windows and Mac only). Thisis only possible at design time.<br />

SUPALWAYS If this field is set to .T., the line isn't printedif it is blank.<br />

SUPOVFLOW If this is set to .T., the object is printed if thedetail band changes to a new<br />

page.<br />

SUPRPCOL If this field is not 0, the object is printed in thefirst whole band of a new page.<br />

SUPGROUP If this field is not 0, the object is printed whenthe group changes. The group is<br />

defined by the number.<br />

SUPVALCHNG If this field is set to .T., the field is printedonly if it's value changes.<br />

SUPEXPR This field contains the Print-When-statement.<br />

USER This field is reserved for the user and it can beused for any reasons. It doesn't<br />

affect the report at all. (This field isnew in VFP.)<br />

Most of this is undocumented and can be changed byMicrosoft without notice. Also, all these fields have different<br />

meaningson different platforms. In the Visual FoxPro manuals, you can see whichfields are used on which platform,<br />

but the meaning of the fields is undocumented.I tried to list the most important things, but this list isn't<br />

complete.Unfortunately, most of the fieldnames don't tell what they do or that theradius of a shape is stored in the<br />

field offset.<br />

Table 2. Table of commands.


Usable in the global comment-snippet: *:SORTORDER field1, field2, field3,...<br />

This command sorts all fields that are passed as parameters in the orderthey are passed.<br />

Use the same names that you define with *:SORTOBJ *:SORTCOL NumPos<br />

This command defines in which column the first *:SORTORDER field appears. *:SORTSPACE NumSpace<br />

This command defines the size of the space between the *:SORTORDER-fields. *:DOSORT [NameOfSortprog]<br />

You can use this command instead of *:SORTORDER. The difference is *:SORTORDERdefines an unchangeable<br />

sorting order. With *:DOSORT fields can be sortedfrom the user in a nice screen that can be<br />

modified. *:MOVEHEAD x [INCHES}<br />

This command resizes the header by X centimeters or inches. This is veryuseful if you don't know how big your<br />

customers' letterhead is. One of themhas an 3 centimeter header, and the next one is 7 centimeters high. So youdon't<br />

reserve space for the header at all, and you begin your report atthe top-most pixel. Then, you move the header<br />

depending on a variable definedin your options dialog. *:CLASSLIB Drive:\Path\Name.Ext<br />

This defines a standardclasslib. You can define as many standard classlibsas you want. Usable commands in the<br />

local comment-snippet *:DELETE<br />

Deletes the object during generation. *:DELOBJ<br />

Deletes the object after generation. *:UNDERLINE<br />

Adds the underlined attribute to the object. *:RGB_COLOR r,g,b,r,g,b<br />

With this command, you can use all the possible color-values (1-255) *:DEFI_WHEN_CLASS<br />

Generates a class, which contains the PrintWhen statement and the RemoveLineStatus of the current<br />

object. *:USE_WHEN_CLASS<br />

Uses a defined WhenClass on the object. *:DEFI_EXPR_CLASS<br />

Generates a class, which contains the Expression statement of the currentobject. *:USE_EXPR_CLASS<br />

Uses a defined ExpressionClass on the current object. *:DEFI_PICT_CLASS<br />

Generates a class, which contains the Picture statement of the current object. *:USE_PICT_CLASS<br />

Uses a defined PictureClass on the current object. *:FUNCTION<br />

With this command, you can define a function directly in your report. Thisfunction will be added to the<br />

GenRepoX-function library. GenRepoX compilesthis functions "on-the-fly." *:ENDFNCT<br />

This command sets the end of a function. You don't need to use this commandif your don't use other comments after<br />

your function. *:DEFOBJ<br />

With this command, you can define a unique name for your object. *:DETAIL DbfName [FOR filter]<br />

With this command, you can define a detail band for instance in a groupfooter.This command activates the detail<br />

extender. *:IF_COLOR statement COLOR RGB(red,green,blue,red,green,blue)<br />

FONT "Fontname", Fontsize STYLE "FontStyle"<br />

With this command you can change the color and the font of the current objectdepending on your<br />

statement. *:SORTOBJ Objektname<br />

All objects that contain this command can be sorted on the fly. *:POSOVER Def-Objektname [LEFT | CENTER |<br />

RIGHT]<br />

This command positions an object above the object name that is passed asa parameter. *:AS_CLASS ClassName<br />

[OF ClassLib] (new in Version 2.0)<br />

This defines that this object is a SubClass of the Class "ClassName,"which is stored in "ClassLib." *:CLASS<br />

ClassName (new in Version 2.0)<br />

This gives an object a ClassName so that it can be found by other reports. *:CLEAR_EXPRESSION (new in<br />

Version 2.0)<br />

This command clears the expression field of the actual object. You needthis since the report writer doesn't give you<br />

the possibility to leave itempty. *:CLEAR_FONTFACE (new in Version 2.0)<br />

This command deletes the FontFace-information. *:CLEAR_FONTSIZE (new in Version 2.0)<br />

This command deletes the FontSize information. *:CLEAR_FONTSTYLE (new in Version 2.0)<br />

This command deletes the FontStyle information. *:CLEAR_FONTINFO (new in Version 2.0)<br />

This command deletes all Font information. *:EVENT EventName (new in Version 2.0)<br />

With this command, you can create methods that are related to an event. *:ENDEVENT<br />

This command defines the end of an event. Usable commands in the picture-/ expression-snippets and other<br />

fields {{&Variablename}}<br />

The value of the variable "variablename" will be evaluated andexecuted. *:: (new in Version 2.0)


Left Hand or Right Hand?<br />

Whil Hentzen<br />

Last December, Bob Grommes promised articles resulting from encounters with people he met<br />

and things he heard at the Great Lakes Great Database Workshop in Milwaukee the previous<br />

month. I'm happy to announce the start of Steven Black's series on Design Patterns. As you'll<br />

expect if you've seen Steven's work before, this topic is sophisticated and requires some<br />

concentration. Although it might seem like he's talking about Visual FoxPro only, each of the<br />

concepts he unveils has broader applicability -- to your work as a FoxPro 2.x programmer as<br />

well as 3.0, to your work as a software developer, and to your role as a business person.<br />

Another legacy that Bob left me was coping with the flood of responses to his November 1995<br />

editorial, "Honk if You Love 2.x." Dealing with the conundrum of balancing 2.x and Visual<br />

FoxPro articles is probably my most important, and touchy, issue. Reading through the e-mails<br />

and letters has given me new insight into this problem.<br />

Unfortunately, there's no hard and fast answer to this dilemma. As you'd expect, some readers<br />

were vehemently in favor of all Visual FoxPro: "I know how to do what I want in 2.x, but I'm<br />

still learning VFP!" Others felt that VFP was irrelevant and didn't want to read about it. "Most of<br />

my clients are still using DOS, and I'm just getting them to move to Windows. They're not going<br />

to upgrade their hardware to the point where they could run VFP for years." I feel like the guy in<br />

the movie who has two dates for the same evening, and is trying to run back and forth between<br />

the two tables to keep each date happy.<br />

Fortunately, I can at least admit to both dates that I'm trying to serve two masters at the same<br />

time. While it won't be easy, we're going to continue a mix of 2.x and 3.0 articles, but also<br />

include more articles that are either relevant to both (such as Menu Builder or Report Writer<br />

topics), or that discuss techniques that are independent of platform (for example, articles on the<br />

SQL language or algorithms.) Those of you who have yet to move to Visual FoxPro -- don't<br />

despair -- at some point you likely will, and you'll have a trove of useful information to help you<br />

get up to speed quickly. And for the crowd that's running VFP full-steam, take a quick scan<br />

through the 2.x articles -- you may find an idea that you can adapt to your current work after all.<br />

The 10 Most Common Mistakes in Visual<br />

FoxPro<br />

Edward Leafe


Want to learn from the mistakes of others? Visual FoxPro trailblazer Ed Leafe shares pitfalls that<br />

have bitten others. Read this so your own development goes smoother.<br />

Okay, so you've made the move to Visual FoxPro and are working on your first VFP application<br />

. You've read all about object orientation, you've learned about the event model, you've even<br />

read every message on CompuServe's VFOX forum. You've done a detailed object oriented<br />

analysis, followed by a brilliant object-oriented design, and now it should be smooth sailing,<br />

right?<br />

Not necessarily. Though Visual FoxPro avoids nearly all of the annoying things that were in<br />

prior versions, such as READ levels, the Foundation READ, limited variable scoping, and poor<br />

BROWSE integration with a READ, there are some new "gotchas" that will inevitably sneak up<br />

and bite you. I've put together a list of the ones that have most frequently stung me and those that<br />

have been mentioned by other developers, in the hopes that it might make your development<br />

tasks a little easier.<br />

1. Forgetting to reference object methods and properties with "This."<br />

On the surface, it seems that an object should be able to correctly understand a statement such as<br />

nProperty = 7, assuming that the object has a property called "nProperty." But think about it for a<br />

second: this is the same syntax that prior versions of FoxPro have used for assigning a value to a<br />

memory variable. In those versions, if the memvar didn't exist, it was created by such a<br />

statement. Visual FoxPro retains this behavior (thankfully).<br />

Also, consider this expression: IF cName = "Melvin". What is "cName"? Is it a field in the<br />

currently selected table? Is it a memory variable? This is an ambiguity that FoxPro developers<br />

have had to be aware of in the past. You would qualify such a statement by prefixing "cName"<br />

with either "Alias." if it were a field from an open table, or "m." if it were a memory variable.<br />

Now, in Visual FoxPro, you have a third possibility, that this could be a property of an object.<br />

You deal with this the same way, by adding a qualifying prefix denoting the object. If the code<br />

occurs in a method of that object, the prefix would be "This."; if it were a property of the form in<br />

which that object exists, it would be "ThisForm.".<br />

With method calls, there are slightly different reasons. It's valid to call a method with the<br />

following code:<br />

This.MyCoolMethod<br />

There's no equal sign required at the beginning of the statement, as there would be if this were a<br />

UDF call. Now what if you were to leave out the "This."? FoxPro would just see the word<br />

"MyCoolMethod" and choke, generating the error message "Unrecognized Command Verb." By<br />

adding the qualifier "This." or "ThisForm.", Visual FoxPro now knows how to resolve the<br />

statement.<br />

2. Forgetting to reference properties/methods of the container with


"This.Parent"<br />

One of the handiest things about object-oriented design is the ability to take a group of controls<br />

that work together, put them in a container, save the container as a class, and then drop that<br />

container class on a form whenever needed. Common examples would be name and address<br />

entry fields, search objects, and business objects. Many times, all the controls in the container<br />

will have to be able to reference the same properties or methods in order to work together, so<br />

you would make them properties/methods of the container class. If you have, say, a command<br />

button in the container and it needs to call a method of the container called "Cogitate," the<br />

correct reference would be:<br />

This.Parent.Cogitate()<br />

It's a common mistake to leave out the "Parent." portion of the method reference, and simply<br />

write the following:<br />

"This.Cogitate()"<br />

This will generate a helpful error message: "Property `cogitate' not found."<br />

A more difficult error to debug is when the object and its container have methods with the same<br />

name (a very common situation). If you leave out the "Parent." part of the method call, no error<br />

message is generated since there is a method in the object by that name, but the wrong code gets<br />

executed!<br />

3. Forgetting to add ".Value" when referencing a control's current state<br />

Let's say you have a text box whose name is "txtLastName," and your business model requires<br />

that all entries have the Last Name field filled in. So in the Save routine, you write the following<br />

validation code:<br />

IF EMPTY(Thisform.txtLastName)<br />

WAIT WINDOW "The Last Name field must be filled in"<br />

Thisform.txtLastName.SetFocus()<br />

ENDIF<br />

When you try to run that code, you'll get an error message stating "Function argument value,<br />

type or count is invalid," and the "IF EMPTY" line will be highlighted. The reason?<br />

"Thisform.txtLastName" is an object. What you want to check is the value of that object, which<br />

in this case would be any text entered into that text box. By changing the line to:<br />

IF EMPTY(Thisform.txtLastName.Value)


things work as desired.<br />

4. Confusing the various meanings of "Parent"<br />

We're all used to the term "parent-child" when describing a relation set up between tables: the<br />

"parent" table is the controlling table in the relationship (obviously, the coiner of this term didn't<br />

have kids!), and the "child" tables depend on the current record in the parent table.<br />

Visual FoxPro has added two more meanings to the word "parent." The first, and probably the<br />

poorest choice of terminology in the product, is the "ParentClass" property, which refers to the<br />

class from which the class in question was subclassed. While there is no such thing as a standard<br />

for object-oriented terminology, nearly everyone uses the term "SuperClass" for this, but for<br />

some reason Visual FoxPro calls it "ParentClass."<br />

The second use of the term is the "Parent" property, which has absolutely nothing to do with the<br />

"ParentClass" property: it refers to the container in which the object resides. It's a handy<br />

property, allowing objects in the same container to reference each other using<br />

This.Parent.OtherObject, no matter what container (if any) their Parent container happens to be<br />

in.<br />

5. Scoping variables to forms<br />

The scoping variables to forms problem is the result of one of the many mindset shifts that are<br />

required when switching from procedural code to objects. In 2.x, if you needed to define a<br />

variable that would be available to all the controls and procedures of a screen, you would create<br />

it in the SETUP code, and its scope would be the entire SPR. Any control's WHEN or VALID<br />

could reference it, as could any procedure in the CLEANUP section.<br />

Now you create a form in Visual FoxPro, and need such a memory variable. So you open up the<br />

INIT() method, and cheerfully type in the following:<br />

PRIVATE lnIncome<br />

lnIncome = 1000000<br />

Then in one of your command buttons, type the following:<br />

IF lnIncome > 50000<br />

WAIT WINDOW "Hey, can I borrow a few bucks?"<br />

ENDIF<br />

When you go to run this form, you'll get the error message "Variable `lnIncome' not found."<br />

What happened? You declared and assigned the variable in the INIT method, and so it must have<br />

run before your command button code. Where did it go?


The answer is that it went out of scope. When an object's methods execute, they are analogous to<br />

calls to UDFs: any memvars declared in those lower-level procedures go out of scope when the<br />

procedure ends. When an object instantiates, its INIT method is executed. Any variables created<br />

there (except public memvars) go out of scope and are released as soon as the INIT code is<br />

completed. Thus none of the objects' other methods can "see" that memvar.<br />

So why not make memvars that all objects in the form refer to PUBLIC? You could then release<br />

them in the DESTROY method, and things would sort of work the way they did before. While<br />

this approach will work, it has two main drawbacks. First, it goes against the principle of<br />

encapsulation: that every object should keep all its "inner workings" from spilling out into the<br />

general environment. An object shouldn't have to worry about checking to see if something else<br />

created a memvar with the same name, and it certainly shouldn't overwrite any memvars that<br />

happen to already exist. Second, it simply won't work if you want to take advantage of some of<br />

the coolest aspects of forms, namely multiple instances with private data sessions. If a form is<br />

relying on PUBLIC memvars, all other instances will be relying on those same memvars.<br />

Changing the value in any form will change it for all forms. This defeats the whole purpose of<br />

having multiple instances of the form.<br />

The answer is to start thinking of the form as an object, not as a chunk of code like an SPR. Any<br />

variable that "belongs" to an object should be a property of that object. In this case, by making<br />

the memvar a property of the form instead, every object on the form can reference it by using the<br />

Thisform.cProperty notation, it will "disappear" when the form is released, it won't change<br />

anything in the general environment, and each instance of a form will have its own copy of the<br />

property.<br />

6. Trying to close buffered tables without committing changes<br />

One of the most powerful improvements in Visual FoxPro is the ability of the product to handle<br />

data buffering itself, without the programmer having to do all sorts of tricks to copydata. By<br />

simply setting the BufferMode property of a form, or the BufferModeOverride property of a<br />

cursor in the DataEnvironment, Visual FoxPro will handle all the data buffering aspects for you.<br />

Well, except one. If you make changes to a buffered cursor (either a table or a view), either<br />

directly via REPLACE or by modifying the value of a control whose ControlSource property is<br />

set to a field in a buffered cursor, Visual FoxPro will mark that table as "modified." This will<br />

happen even if you don't change the value! For example, if the record has a value in the<br />

cFirstName field of "Fabian," and your code contains the following line Visual FoxPro will mark<br />

that record as modified, even though the value is the same as the original:<br />

REPLACE cFirstName WITH "Fabian"<br />

Assigning a value, even an identical one, will suffice to mark the record as modified in the eyes<br />

of VFP.<br />

If you then try to close the cursor or issue a REQUERY() in the case of a view, FoxPro will


generate the error message "Table Buffer for alias `Customer' contains uncommitted changes." In<br />

other words, Visual FoxPro will handle all the buffering for you, but you have to be sure to tell it<br />

what to do with that buffered data. This is handled through the use of either the<br />

TABLEREVERT() or TABLEUPDATE() functions to discard changes or save them,<br />

respectively. Be sure to include these functions at all exit points from any form that uses<br />

buffered data.<br />

7. Using the AS clause in views and queries<br />

The problem with using the AS clause in views and queries isn't new in Visual FoxPro; fact is,<br />

this problem goes all the way back to the original RQBE in FoxPro 2.0. The reason I mention it<br />

here is that, with the introduction of views in VFP, the potential damage from this bug is<br />

magnified greatly. After all, nobody used RQBE for more than SQL training wheels; it helped<br />

you learn SQL syntax, but it was so limited that within a short while you were writing SQL<br />

statements by hand. However, with the addition of the ability to create updatable views in the<br />

View Designer, you now have a pretty powerful and useful tool at your disposal. Yet this bug,<br />

which has existed since 2.0, remains.<br />

For those who haven't been bitten by this bug yet, if you create a view or query that contains a<br />

complex field, such as a SUM() or a field based on more than one field in the source tables,<br />

standard SQL provides the ability to name the resultant field by following the definition with the<br />

AS clause:<br />

SELECT cID, cFirst + cLast AS cFullName ;<br />

FROM Clients<br />

If you do this in the View Designer, it will faithfully record your AS clause, and the resultant<br />

view will have a field named "cFullName." You can then use that field name in any form or<br />

control that references this view. However, if you go back into the View Designer to make any<br />

change whatsoever, even one that doesn't modify the cFullName field, VFP will quietly erase the<br />

"AS cFullName" part of the view definition, and now all your code that refers to this field will<br />

generate errors, because Visual FoxPro has renamed the field to something really useful like<br />

"Exp_1."<br />

The solution? I'm tempted to say, "Don't use the View Designer," but it's a handy tool, especially<br />

for setting up updatable views. What I've resorted to doing is defining the view by code instead<br />

of visually. This works quite well, except that I have to remember to do it each time I modify one<br />

of my views. The way to do this in code follows:<br />

CREATE SQL VIEW ClientView AS ;<br />

SELECT cID, cFirst + cLast AS cFullName ;<br />

FROM Clients<br />

This is far from an elegant solution. The only elegant solution is to have the bug fixed so that


things work the way they are supposed to.<br />

8. Forgetting the LockScreen property of forms<br />

One of the complaints I hear a lot about VFP is that if you have a form with a lot of controls on<br />

it, refreshing the form can be painfully slow. That's very true: watching a complex form redraw<br />

each pixel of each control can really seem to run in slow motion. The answer is to set the form's<br />

LockScreen property to .T. before refreshing, and then setting it back to .F. afterwards. This lets<br />

FoxPro update all the screen objects in the background, and then display only the "final<br />

product." The results are much snappier.<br />

One caveat: remember to set LockScreen back to .F. when you're done! The form is basically<br />

useless to the user while it's locked, and they won't be happy.<br />

9. Using a numeric as the Value in a Combo Box<br />

Combo boxes are a cool addition to Visual FoxPro, but there's one aspect of them that causes a<br />

lot of confusion: the Value property of the combo box can only be type Character when referring<br />

to the literal contents of the combo box. The problem stems from the fact that combo boxes are<br />

just that: a combination of a text box and a list. You can reference an item in this list by either<br />

the actual value of that item, or by its position in the list. However, there's no way to specifically<br />

tell Visual FoxPro which reference method you would like to use. It makes the assumption that if<br />

you use a numeric (including type Integer) as the Value, you are referring to the item's position<br />

in the list, not its contents. This is especially problematic if the combo box has its ControlSource<br />

property set to a field whose type is Numeric or Integer. In that case, there's no way to refer to<br />

the actual value of the contents of the field, only to its position!<br />

For example, let's say I'm using a type Integer field as the Primary Key for my table. On my<br />

form, I create a combo box with two columns: the first will hold a descriptive text field from<br />

which the user will select an item, and the second will be my PK field. I set the BoundColumn<br />

property to 2 so that the combo box is tied to my PK field, and then set the column widths to<br />

200,0 so that only the descriptive field displays. However, if the user selects, say, the third item<br />

in the list, Visual FoxPro will replace my PK field with a value of 3, even if the original value<br />

was something like 104315! This can have disastrous consequences when using PK fields, since<br />

referential integrity can be easily destroyed.<br />

There's no "trick" to get around this since this behavior was designed into the control; any<br />

reference to a numeric is automatically translated into a position reference. The only option is to<br />

translate everything to characters ahead of time, such as by creating an array based on<br />

STR(nField), where nField is the numeric field, and then use VAL() to translate back to numeric.<br />

This will exact a performance hit, though, and also prohibit you from using the ControlSource<br />

property to automatically link the control to a field in your tables. Your best bet is to limit the<br />

use of combo boxes to fields of type character. For this reason, many developers have decided to<br />

stick with using character values for Primary Keys, even though Integers would seem to be much<br />

faster and easier to handle.


10. Debugging: not being able to set breakpoints in the Trace window<br />

In 2.x, you could run a screen, wait until it got to the READ line, pull up the Trace window, then<br />

scroll through it to the section of code you were debugging, and then set a breakpoint there. In<br />

Visual FoxPro, if you DO FORM Slag.SCX and then pull up the Trace Window, you don't see<br />

anything! What happened to the code?<br />

The answer is a key difference between FoxPro 2.x and Visual FoxPro. In 2.x, the READ served<br />

as the wait state, which allowed the procedural code to pause long enough for you to enter text,<br />

scroll through lists, and click on buttons. In the object-oriented world of VFP, however, no such<br />

gimmickry is needed. Once the form and its controls are instantiated, they exist and can respond<br />

to events without the need to pause the system, since they don't rely on a set of procedural code.<br />

So now when you view the Trace Window, you don't see any code because none is executing!<br />

So you're out of luck, now, right? No way! You just have to be a little sneakier in approaching<br />

your debugging. My favorite method is to set the break points in the Debug window using the<br />

PROG() function. For example, if I have a command button whose CLICK method I want to<br />

examine, I can put "CLICK" $ PROG() as the expression in the Debug window, set a breakpoint<br />

on that expression and then run the form. (PROG always returns uppercase). When any method<br />

with "CLICK" in its name is executed, the system will pause and you can begin stepping through<br />

in the Trace window.<br />

But what if you have many sorts of CLICK methods (or DBLCLICK, or RIGHTCLICK, for that<br />

matter) that get executed before you get to the one in which you are interested? In that case, you<br />

can refine the breakpoint expression further by including the name of the control to further limit<br />

the Debug break expression. If, for example, the command button in which you are interested is<br />

named "cmdRecalculate," make the expression in the Debug window :<br />

"CLICK" $ PROG() AND "CMDRECALCULATE" $ PROG()<br />

This takes advantage of the fact that PROG() will return the fully qualified object name as well<br />

as the particular method's name. In this case, if the command button was on a page in a<br />

pageframe, PROG() would return the following:<br />

"FRMBASE.PGFFRED.PAGE1.CMDRECALCULATE.CLICK"<br />

You could even make the following to limit it just to CLICK methods:<br />

expression ".CLICK" $ PROG()<br />

11. Converting 2.x code<br />

In Visual FoxPro, you have the option of taking your 2.x applications and converting them to run


under Visual FoxPro. It's tempting to think that you can parlay all your work on a 2.x application<br />

into a fully object-oriented, event-driven application by clicking on a menu choice, but it isn't<br />

necessarily so. Sure, the converter will spit out something that will pretty much run under Visual<br />

FoxPro, but it will contain all the limitations and maintenance headaches of the 2.x application,<br />

require more resources, and run slower to boot.<br />

If you're thinking of converting a 2.x app to 3.0, my advice is simple: don't! Re-write and<br />

re-design it! The Foundation READ is dead! READ levels are dead! BROWSEs are dead!<br />

SCATTER and GATHER are dead! (well, okay, not completely, but hey, I'm on a roll here!). If<br />

you think about it, a lot of what went into a FoxPro 2.x application was simply programming<br />

tricks that served to get around all the built-in limitations of the event model. All those tricks<br />

will only slow you down now. If you're planning on moving into Visual FoxPro, take the time to<br />

rethink the application before plunging ahead. If you don't feel you have the time, then keep it in<br />

2.x: it'll still run fine. Remember, older versions of software don't magically stop working when<br />

a new release comes out. Nobody says you have to upgrade to the latest and greatest right away.<br />

I know, this was supposed to be a list of 10 common mistakes, and I threw in 11. Consider it a<br />

bonus! I hope that this will help you avoid the common pitfalls as you dive into Visual FoxPro<br />

development. I can't tell you how many times I've forgotten to add ".Value" when referencing a<br />

control, though VFP is always quick to remind me!<br />

Ed Leafe is an independent consultant who lives in Hackensack, New Jersey with his wife and two sons.<br />

He specializes in Visual Codebook 3.0 development in Visual FoxPro, and cross-platform systems<br />

development in FoxPro 2.6. He is the author of Xplat, a commercial Mac < - > Windows transporter<br />

replacement for FoxPro 2.6. Ed was recently named a Microsoft MVP. He teaches Visual FoxPro classes,<br />

has spoken at conferences and different user groups, and has written articles for several FoxPro<br />

publications. CompuServe 72530,1175.<br />

Make the Form Wizard Do What You Want<br />

It to Do<br />

William J. O'Connor<br />

The Visual FoxPro Form Wizard is a handy tool for making quick screens. It's also a wonderful<br />

learning tool. The WIZSTYLE class library comes with excellent examples of code that can be used<br />

within your own forms outside the Form Wizard. The Form Wizard is also highly configurable.<br />

Here's what you can do to change and improve the Form Wizard classes and even the Form Wizard<br />

itself.<br />

The Form Wizard is many programmers' introduction to the world of object-oriented Visual<br />

FoxPro programming. It can be an excellent learning tool, but following the code behind it can<br />

be daunting even for expert programmers. In this article, I'll attempt to demystify the structure of


the Form Wizard classes by going into the properties and procedures in the wizard. I'll pay<br />

special attention to the Form classes and the TxtBtns navigation class. This article uses the<br />

updated wizards, which can be found with Visual FoxPro 3.0b and also in the file<br />

VFP30WIZ.EXE on the Microsoft Home Page (), the Microsoft Library on CompuServe (GO<br />

MSL) and through Anonymous FTP (ftp.microsoft.com/softlib/msfiles).<br />

I'll show you how to make direct changes to the Form Wizard classes. Before you make any of<br />

the changes in this article, make a backup copy of WIZSTYLE.VCX and WIZSTYLE.VCT! If you<br />

make an improper change to a Form Wizard class, you could end up causing all the forms that<br />

use the Form Wizard classes to fail. If possible, when changing one of the classes contained in<br />

the Form Wizard, subclass the object and make changes to the subclass not the original class.<br />

This gives you a way to fall back if you make a mistake.<br />

Setting up the Form Wizard<br />

The Form Wizard classes are set up with a base class where all the custom properties are created.<br />

Subclasses are created from the base classes. Custom properties are set and style elements are<br />

added. Let's look at how the class structure is set up. Most classes have two levels of subclasses<br />

(see Table 1).<br />

Table 1. Form Wizard classes.<br />

Wizard Style Visual FoxPro 3.0 Class Inserted Form Wizard Class<br />

Base Form BaseForm<br />

TextBox None<br />

CheckBox None<br />

EditBox None<br />

OLEBoundControl None<br />

Box Style Form BoxForm<br />

TextBox BoxField<br />

CheckBox BoxLogic<br />

EditBox BoxMemo<br />

OLEBoundControl BoxOLE<br />

Chisel Style Form ChiselForm<br />

TextBox ChiselField<br />

CheckBox ChiselLogic<br />

EditBox ChiselMemo<br />

OLEBoundControl ChiselOLE<br />

Embossed Style Form EmbossedForm<br />

TextBox EmbossedField


CheckBox EmbossedLogic<br />

EditBox EmbossedMemo<br />

OLEBoundControl EmbossedOLE<br />

Shadow Style Form ShadowForm<br />

TextBox ShadowField<br />

CheckBox ShadowLogic<br />

EditBox ShadowMemo<br />

OLEBoundControl ShadowOLE<br />

Standard Style Form StandardForm<br />

Changing the wizard itself<br />

TextBox StandardField<br />

CheckBox StandardLogic<br />

EditBox StandardMemo<br />

OLEBoundControl StandardOLE<br />

It's not possible to change the wizard since Microsoft views the wizards as a proprietary<br />

technology. They don't include the source code and decompiling them is a violation of the User<br />

Agreement. You can make changes to the classes the wizard uses to create forms. This is<br />

acceptable and encouraged.<br />

Form Wizard limitations and benefits<br />

The Form Wizard classes, while very powerful tools, have some very serious limitations and<br />

bugs. First, they aren't designed to be used outside of the Form Wizard. Because of this, the<br />

Wizard coders didn't include support for any object that the Form Wizard wouldn't create. So, if<br />

you're looking to add a spinner, option group, command button or group, list box or combo box<br />

to your form, you'll be out of luck. The Wizard also doesn't properly revert non-buffered tables.<br />

As a benefit, the Form Wizard is very powerful and now even configurable. The Form Wizard<br />

allows you to specify any of the object types it uses. The Form Wizard is also an excellent<br />

learning tool. It's designed to handle updatable and non-updatable views, data environments, and<br />

buffered tables outside of data environments.<br />

Changing the objects used by the Form Wizard<br />

Inside the Form Wizard form classes are properties that the Form Wizard reads to determine the<br />

classes that will be instantiated. These properties are WizField for text boxes, WizGrid for grids,<br />

WizLabel for labels, WizLogic for check boxes, WizMemo for edit boxes, and WizOLE for OLE<br />

bound controls. By adding one of your classes to the WIZSTYLE class library and then changing<br />

the appropriate property on one of the forms, your class will be instantiated when you run the<br />

Form Wizard for that style. Please remember that these properties are all overridden in the<br />

"stylized" form classes. You can't change one of these properties in the BaseForm class and


expect to have the change inherited by its subclasses.<br />

For example, I've created for my non-wizard forms a text box class called oTextBox, which<br />

contains the ability to verify data, search, justify data, require values, and a bunch of other<br />

functions that make life easier on me as a programmer. If I want to have the Form Wizard use<br />

my class when it generates Standard style forms, I first add my class to the WIZSTYLE class<br />

library (The Form Wizard won't use classes outside the WIZSTYLE class library), then open the<br />

StandardForm class and change the WizField property to otextbox. Save and close the<br />

StandardForm class and voilá, the Form Wizard will now use my class when it generates<br />

Standard style forms. The same applies to all the classes that can be instantiated by the Form<br />

Wizard.<br />

Another feature of the Form Wizard is that it will use Edit Boxes for character fields over a<br />

certain length. This length is controlled by the WizMaxCharField property. By changing this<br />

property, you can make the Form Wizard use text boxes for longer fields than are normally<br />

supported. I usually set this value to 60 as opposed to the default of 45.<br />

Here's one caveat: The Form Wizard that shipped with Visual FoxPro 3.0 also had these<br />

properties, but they didn't have any effect on the objects instantiated. You must make certain that<br />

you have the updated wizards.<br />

TxtBtns<br />

TxtBtns contains the navigator code for the Form Wizard. Both of the other navigator classes,<br />

PictBtns and VertTxtBtns, are subclassed off this class. It's unarguably the most complex of the<br />

wizard classes. It handles enabling and disabling objects on the form; adding; editing; saving and<br />

reverting data; moving through the table; printing; searching and closing a form. These are a lot<br />

of functions in one small place. TxtBtns is a container with command buttons in it (see Figure 1).<br />

Each button has one or two functions that it handles.<br />

The TxtBtns command buttons<br />

TxtBtns has 10 command buttons instantiated into four sections. The first four buttons handle<br />

record by record navigation through the table. The second section handles searching and printing<br />

of records from the table. The third handles changing data within the table, and the fourth closes<br />

the current form and cleans up its environment. The most critical components of the TxtBtns<br />

class are the Add and Edit buttons, which handle saving and reverting data as well.<br />

SetAllProp<br />

I'm not going to go through all the methods, but I'll concentrate on a few critical methods. The<br />

first is SetAllProp. The SetAllProp method is located in the TxtBtns container. It handles the<br />

enabling and disabling of objects on the form and is the source of most of the complaints from<br />

people adding objects to a wizard generated form. The updated Form Wizard contains an<br />

important improvement: it now handles page frames. Previously, if you added a page frame to a<br />

wizard generated form, any objects on the page frame wouldn't get enabled or disabled like the<br />

objects not contained on the page frame. To take care of this problem, I uploaded to


CompuServe's VFOX forum a SetAllProp replacement. This file, SETALL2.ZIP, contains fixes<br />

for page frames and all the non-supported objects. The downside to the updated Form Wizard is<br />

that it still handles only grids, text boxes, check boxes, edit boxes, and OLE Bound Controls.<br />

The SetAllProp method is an example of recursive code. The method continually calls itself as it<br />

goes through the container classes on the form. It checks to see if the object it has encountered is<br />

one of its supported classes. If it is, it will enable or disable the control by setting its Enabled<br />

property according to whether the user is adding/editing a record. It handles grids and edit boxes<br />

differently from the other controls. Edit boxes aren't enabled and disabled, but are set to read<br />

only or read write depending on if the user is editing or adding records. It also changes the<br />

ForeColor and BackColor properties to their Disabled counterparts. This allows the user to scroll<br />

through the edit box, but not to change the data in the edit box. Grids are handled similarly, with<br />

the DeleteMark property also being set.<br />

I've also made one additional change to all of my classes. I've added a property: lSetIgnore. This<br />

property, used with the replacement SetAllProp method, allows you to have controls that are<br />

ignored by SetAllProp.<br />

I was assisted in modifying the SetAllProp method by Stuart Dee of Morph Computing. I'm<br />

indebted to him for his invaluable assistance. The new SetAllProp method, which supports all<br />

the objects Visual FoxPro supports, follows:


* SETALLPROP<br />

*<br />

* This is a replacement for the SETALLPROP<br />

* method in the TxtBtns class. The main<br />

* improvements that I've added deal with support<br />

* for all objects and containers. Also, if you<br />

* add a property to your CheckBox, TextBox,<br />

* OleBoundControl, ComboBox, CommandButton,<br />

* OptionButton, and EditBox classes, you can prevent<br />

* them from being enabled or disabled. Please note<br />

* that the code checks for the existence of these<br />

* properties, so if you don't have them, no errors<br />

* will occur.<br />

*<br />

* New Properties :<br />

* lIgnore - Affects CheckBox, TextBox, OleBoundControl,<br />

* ComboBox, EditBox, Spinner, CommandButton<br />

* Controls. Will prevent the<br />

* aforementioned control from being enabled<br />

* or disabled by this method.<br />

* Any comments or questions should be sent<br />

* to William J. O'Connor, CIS ID 75231,3643<br />

* Now Supports Command and Option group Controls - S Dee.<br />

LPARAMETER oContainer<br />

LOCAL i, oControlParent, llUsePages,llUseGroup oObject, ;<br />

lnControlCount<br />

* 950609 WJO - All references to paging were added<br />

* by Bill O'Connor<br />

IF PARAMETERS() = 0<br />

m.oControlParent = _SCREEN.ACTIVEFORM<br />

ELSE<br />

m.oControlParent = m.oContainer<br />

ENDIF<br />

llUsePages = .F.<br />

llUseGroup = .F.<br />

DO CASE<br />

CASE m.oControlParent.Name = THIS.Name ;<br />

AND m.oControlParent.Parent.Name = THIS.Parent.Name<br />

* Ignore any attempt by this method to change any<br />

* of the other controls within the TxtBtns class.<br />

RETURN<br />

* Can't use ATC Function here Gets<br />

* Confused With Page - SDee<br />

CASE UPPER(m.oControlParent.BaseClass) = "PAGEFRAME"<br />

lnControlCount = m.oControlParent.PageCount<br />

llUsePages = .T.<br />

CASE UPPER(m.oControlParent.Baseclass) = "OPTIONGROUP"<br />

* O ti G C d Add d B S D


Fixing the revert routine<br />

The revert routine is contained in the Edit button. It handles most cases except where you are<br />

using a data environment and opening a non-buffered table. If the current alias is ever the<br />

non-buffered table, you'll encounter an error as the routine attempts to TABLEREVERT() the<br />

non-buffered table. The offending section of code is as follows:<br />

IF THIS.Parent.UseDataEnv<br />

SELECT (THIS.Parent.OldAlias)<br />

=TableRevert(.T.)<br />

IF !EMPTY(THIS.Parent.GridAlias)<br />

SELECT (THIS.Parent.GridAlias)<br />

=TableRevert(.T.)<br />

ENDIF<br />

ELSE<br />

DIMENSION aTablesUsed[1]<br />

m.nTablesUsed = AUSED(aTablesUsed)<br />

FOR i = 1 TO m.nTablesUsed<br />

=TableRevert(.T.,aTablesUsed[m.i,1])<br />

ENDFOR<br />

ENDIF<br />

This code section can be changed by adding a check for the buffering mode of the current alias<br />

and not allowing a table revert when there is no buffering:<br />

IF THIS.Parent.UseDataEnv<br />

SELECT (THIS.Parent.OldAlias)<br />

IF CURSORGETPROP("Buffering") > 1<br />

=TableRevert(.T.)<br />

IF !EMPTY(THIS.Parent.GridAlias)<br />

SELECT (THIS.Parent.GridAlias)<br />

=TableRevert(.T.)<br />

ENDIF<br />

ENDIF<br />

ELSE<br />

DIMENSION aTablesUsed[1]<br />

m.nTablesUsed = AUSED(aTablesUsed)<br />

FOR i = 1 TO m.nTablesUsed<br />

=TableRevert(.T.,aTablesUsed[m.i,1])<br />

ENDFOR<br />

ENDIF<br />

Summary<br />

The Form Wizard can be a wonderful tool for quickly designing and prototyping forms. The<br />

changes between the original Form Wizard and the updated one have made it extremely<br />

customizable. Even if you are an advanced programmer, reading the code in the Form Wizard


class library can be illuminating and instructive. For those just beginning their climb up Visual<br />

FoxPro's learning curve, the Form Wizard and its class library provide a place to start.<br />

William O'Connor is the senior programmer at ContrAcct Systems Corporation in Naperville, Illinois. He<br />

has been programming in Xbase for 10 years and in FoxPro for four. William is active on the VFOX<br />

Forum on CompuServe. CompuServe 75231,3643<br />

Migrate to Visual FoxPro, Display Calculated<br />

Columns in a Grid, and More<br />

John V. Petersen<br />

FoxPro 2.x to Visual FoxPro 3.0 Migration<br />

In FoxPro 2.x I manipulated the _CUROBJ environmental variable when I needed to make<br />

a specific screen control active. The code looked like the following:<br />

_CUROBJ = OBJNUM(mvar)<br />

How is this done in VFP?<br />

When you need to make a specific control active in VFP, you invoke its SetFocus() Method. The<br />

only information you need to know is the object's name and its path in the containership<br />

hierarchy. Assume you have a TextBox control named txtName on Page1 of PageFrame1the<br />

following code will make it the active control:<br />

THISFORM.PageFrame1.Page1.txtName.SetFocus()<br />

It's important to note that as a consequence of firing an object's SetFocus() method, the object's<br />

When() event fires. If the object's When() evaluates to .T., the object's GotFocus() event will fire.<br />

If you find you aren't getting the desired results, be sure to examine the code in these method<br />

snippets.<br />

Calculated Columns in Browse<br />

How can I display calculated columns in a grid control the way I did in a Browse in 2.x?<br />

The key lies in the ControlSource property of the Column Control. Normally, you place a single<br />

field reference in this property. You can also use the ControlSource property to display<br />

calculations as well. For example, assume you have a grid with three columns: price, quantity,<br />

and extension. The extension is a result of price times quantity. The ControlSource property for


the first two columns is price and quantity, respectively. For the third column, enter<br />

price*quantity.<br />

Anytime the price or quantity changes, the calculated third column will automatically reflect the<br />

new amount. The only other property of interest is ReadOnly. Most likely, you'll want to change<br />

this to .T. for your calculated grid columns.<br />

Grouping Controls<br />

I really liked the capability of grouping several controls together for actions such as<br />

centering in FoxPro 2.x. Its a shame we lost this native capability in VFP. How can I get<br />

this capability back in VFP?<br />

I too miss this feature. While you can't do the same thing in VFP, you can achieve the same<br />

results. First, you need some mechanism of grouping several controls together. The Container<br />

Control is perfect for this job. Unfortunately, the container control isn't available in the Form<br />

Control Toolbar. Therefore, you need to add a container via code. The following steps will do<br />

the trick:<br />

1. Obtain an object reference to the current design form:<br />

=ASELOBJ(laObjects,1)<br />

2. Add the container to the form:<br />

laObjects[1].AddObject('Container1','Container')<br />

Now, it's just a matter of adding and moving existing controls to the container. Now, if you want<br />

to center several controls relative to the form, all one needs to do is center the container.<br />

Updating List box contents<br />

I have several list box controls with the RowSourceType Property set to Alias and the<br />

RowSource Property referencing a view. When I issue a call to the REQUERY() function<br />

and subsequently make a call to the list boxes Refresh() method, the list box doesn't display<br />

the new data as expected. The only way I can see the new data is to click on the list box. I<br />

would like the visual updates to occur automatically. Is this a bug, and if so, is there a<br />

workaround to this problem?<br />

My guess is that this is a bug. However, there's a simple workaround. Assume you have a list<br />

box called List1 on a form. After the REQUERY(), enter the following code:<br />

THISFORM.List1.RowSource = THISFORM.List1.RowSource


Another interesting behavior occurs when RECC() evaluates to 0 after REQUERY(). The list<br />

box tied to this view will disappear. To make the list box visible again, you need to click the<br />

mouse in the area of the list box. Employing the previous solution avoids this behavior as well.<br />

Initializing Combo box Contents<br />

I have a combo box with the RowSourceType set to Alias and the RowSource referencing a<br />

view. The first column in the view contains the data I wish to display in the combo box. By<br />

default, the combo box doesn't display data. I would like contents of the first record to<br />

display when the form first becomes available to the user. I don't want to use the<br />

ControlSource Property as the combo box isn't tied to a specific data element. How can I<br />

do this?<br />

The key to solving this issue lies with the Value Property. Assume the following: the name of the<br />

combo box is Combo1, the name of table is Customers, and the field to display is custname. In<br />

the Init() of the form, place the following code:<br />

THISFORM.Combo1.Value = customers.custname<br />

When the form displays, your combo box will display the contents of the custname field of the<br />

current record.<br />

Adding Values in Combo Boxes<br />

I have a combo box with the Style Property set to DropDown Combo. If the user enters a<br />

value that doesn't exist in the list, I would like it added to the list.<br />

When you enter a value into a combo box that doesn't exist in the list, the Value property will<br />

evaluate to an empty string and therefore is of no value. To determine what was entered, you'll<br />

need to query the DisplayValue property. The solution to this problem centers on comparing the<br />

DisplayValue Property to items that are contained in the List. The following code in the Valid()<br />

Event will do the trick:<br />

LOCAL lnCount,llExists<br />

FOR lnCount = 1 TO THIS.ListCount<br />

IF THIS.List(lnCount) = THIS.DisplayValue<br />

llExists = .T.<br />

EXIT<br />

ENDIF<br />

ENDFOR<br />

IF !llExists<br />

THIS.AddItem(THIS.DisplayValue)<br />

THIS.Value = THIS.DisplayValue<br />

ENDIF


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 103360,1031.<br />

Extend and Adapt Your Classes with<br />

BRIDGEs<br />

Steven Black<br />

This is the first of a series of articles on object-oriented design patterns and how to apply them in<br />

Visual FoxPro. The study of design patterns is an exciting new fad; one that produced some of last<br />

year's most insightful computer books, a few of which are listed at the end of this article.<br />

In the first few articles of this series, I'll present examples of terrific object-oriented design<br />

patterns that you can probably use immediately. In the months ahead, I'll occasionally pause on<br />

the wider issues of pattern usage and pattern coordination.<br />

What is a design pattern?<br />

A pattern is a recurring solution to a particular problem. A design pattern is a general, but<br />

multifaceted, abstraction of the solution and its applicability. Patterns are found wherever several<br />

classes and their instances collaborate as a system. You don't need to "know" about patterns to<br />

unconsciously create and use them. Some design patterns, when applied under appropriate<br />

conditions to solve the correct sort of problem, have known tendencies to be stable, scaleable,<br />

and coherent. These patterns are just now being identified, analyzed, and cataloged.<br />

A design pattern defines the parts, collaborations, and responsibilities of classes and instances<br />

used in a software subsystem. Thus they abstract the subsystem above the level of classes,<br />

instances, and code. Patterns are all about architectures, their component structures, and all their<br />

nuances. Abstracting known good solutions into patterns, and cataloging of pattern-based design<br />

experience, looks promising as a source of design guidance. If anything, it gives good insight<br />

into the balance of forces surrounding a particular problem.<br />

Pattern language gives us a common shared vocabulary about patterns and their<br />

implementations. Pattern language seeks to capture and communicate object-oriented design<br />

experience. A shared understanding of pattern language can vastly clarify communication among<br />

developers in spite of the complexity of the underlying software. So patterns serve as a guide to<br />

the sensible design of software building blocks, and they help us better communicate and cope.<br />

For more on patterns and specific design patterns, refer to the references at the end of this article.


The BRIDGE pattern<br />

BRIDGE is a scale-independent structural pattern used in many object-oriented situations. The<br />

BRIDGE pattern often serves as a player in other design patterns because it defines an abstract<br />

coupling, meaning how objects interact.<br />

Intent<br />

The BRIDGE decouples an object's public interface ("form") from its implementation (its<br />

"function"), using two (or more) objects where otherwise one might less flexibly suffice.<br />

Separating an object's programming interface from its implementation means that both form and<br />

function objects can be subclassed, extended, or substituted independently. The need for this sort<br />

of inherent flexibility is usually why a BRIDGE pattern occurs.<br />

Also known as<br />

HANDLE AND BODY or ENVELOPE AND LETTER are good descriptive names -- the<br />

dual-object nature of BRIDGE patterns is well conveyed, as is the tight coupling usually found<br />

between the programming interface and implementation objects.<br />

Motivation<br />

A BRIDGE structure is an encapsulated system of players: one (or more) "programming<br />

interface" object and one (or more) implementation object. See Figure 1 for an OMT notation<br />

class diagram. The relationship between them, as controlled by the oIMP object reference, is the<br />

bridge. This BRIDGE pattern in Codebook 3.0 hangs on an aptly named member property. Any<br />

of the cDataBehavior subclasses may be substituted.<br />

Why use a BRIDGE? A class may have several possible implementations. This seems true of<br />

reusable classes, especially those that service a variety of clients. The usual way to manage this<br />

is by extending the class hierarchy using inheritance. After all, inheritance is usually the first<br />

mechanism most folks use to reuse their classes. And why not? In the early going, inheritance is<br />

always a big success.<br />

At the limit, however, inheritance leads to sclerosis -- it doesn't scale particularly well. Simple,<br />

single-object inheritance binds interface and implementation -- you have only one package to<br />

transform into a subclass. This means that interface and implementation aspects can't be<br />

independently varied without extending the class hierarchy. But class hierarchies naturally loses<br />

their inherent adaptability with size and as the number and variety of clients increases. Why? It's<br />

due to side effects, partly. The probability of side-effects in clients increases with the number<br />

and variety of those clients, and correspondingly with the current state of the hierarchy. In a<br />

giant class hierarchy, making a small change near the top can be very expensive. Occasionally<br />

this will limit what can reasonably be done at a given level in a class hierarchy.<br />

The BRIDGE pattern mitigates class sclerosis by decoupling interfaces from implementations,<br />

and putting the abstraction and implementation in separate class hierarchies. This allows the<br />

interface and implementation classes to vary independently, and the inherent substitutability of


subclasses makes the structure intrinsically adaptable (and hence reusable). The bridge in all this<br />

is the relationship between the interface and the implementation objects that together form a<br />

self-supporting system.<br />

The workings among the players in a BRIDGE should be direct and simple: the interface object<br />

forwards client requests to the appropriate implementation object. Tight coupling within the<br />

BRIDGE structure is both common and desirable. The tight coupling is much easier to manage<br />

when all the implementation objects come from the same class, as illustrated in the Codebook<br />

example in Figure 2.<br />

Applicability<br />

The BRIDGE pattern is independent of scale and found nearly everywhere within object<br />

systems, including within other common software design patterns. Look for BRIDGE when you<br />

anticipate future extensions to an object's form or function. In this case, separating form from<br />

function can lead to new adaptations without affecting existing client code:<br />

• You want to avoid extending a class hierarchy for the sake of new implementations. The<br />

BRIDGE pattern is an effective alternative to an undesirable explosion in the number of<br />

subclasses in a given class hierarchy.<br />

• You want to choose a specific behavior at run-time. Using a BRIDGE can drastically<br />

lessen the need for design-time divine insight about future applications of this class.<br />

• You need to improve an existing design. The separation of form and function duties is<br />

normally invisible to clients, so a BRIDGE object can usually be successfully substituted<br />

into existing but inflexible code.<br />

• You want to defer object overhead expenses until they are needed instead of paying in<br />

full up-front. A smart BRIDGE can provide this "pay-as-you-go" behavior by<br />

instantiating its implementation objects only when (and if) required.<br />

• Hiding implementation details is desirable in spite of the need to fully disclose a<br />

programming interface. You can give other developers what they need to integrate your<br />

objects without divulging proprietary implementation code.<br />

• Reusing implementation code is made difficult by the need to repeatedly adapt it's<br />

programming interface. You can more easily serve diverse clients if the extensibility of<br />

an object's programming interface is separate from it's functionality.<br />

• A backpointer from the implementation to the interface object is occasionally desirable.<br />

This implies, however, that this implementation object must possess an interface of its<br />

own, which is easier to manage if the implementation object is itself a bridge. Bridges,<br />

you see, are almost infinitely scaleable and applicable.<br />

Visual FoxPro samples<br />

Now we'll look at three simple ways BRIDGE patterns can be built in FoxPro: First with


member properties. Second we'll do more flexible implementations using member arrays, and<br />

third using object composition within containers.<br />

Member Property BRIDGE<br />

One way to build a BRIDGE is diagrammed in Figure 4. Here an interface member property<br />

contains a reference to an implementation object.<br />

In general terms, here's how such systems are constructed in VFP. What follows is a simple<br />

illustrative class that provides WAIT WINDOW services.


Listing 1. A simple BRIDGE using a member property to reference the<br />

implementation object.<br />

XX= CREATEOBJECT( "WaitMsgServer")<br />

XX.Execute("This, for now, is in a WAIT WINDOW")<br />

DEFINE CLASS WaitMsgServer AS MsgInterface<br />

FUNCTION Init( txPassed)<br />

*-- Load an interface object<br />

THIS.aImp= CREATEOBJECT("WaitMsg")<br />

ENDFUNC<br />

FUNCTION Execute( txPassed)<br />

*-- Pass the request along<br />

THIS.aImp.Execute( txPassed)<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS WaitMsg AS MsgImplementation<br />

FUNCTION Show( txPara1)<br />

WAIT WINDOW THIS.cMessage<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS MsgInterface AS CUSTOM<br />

*-- Abstract message interface class<br />

aImp= .NULL.<br />

FUNCTION Execute( txPassed)<br />

*-- Abstract<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE Class MsgImplementation AS Custom<br />

*-- Abstract message implementation class<br />

cMessage= ''<br />

FUNCTION Execute( tcPassed)<br />

THIS.cMessage=tcPassed<br />

THIS.Show()<br />

ENDFUNC<br />

FUNCTION Show<br />

*-- Abstract<br />

ENDFUNC<br />

ENDDEFINE<br />

Another example of this sort of BRIDGE is found in Codebook where the cBizobj (business<br />

object) class uses an object of the cDataBehavior class to implement the usual table navigation<br />

and record-processing functions. The diagram in illustrates this.<br />

Member Array BRIDGE<br />

Starting from the Member Property BRIDGE, it's a short stretch to provide multiple


simultaneous implementations by using multiple member properties or, as described below,<br />

member arrays. The diagram in Figure 3 shows the use of multiple arrays that map to multiple<br />

implementations.<br />

Listing 1 is modified in Listing 2 to support four different types of dialog boxes to extend the<br />

legacy WAIT WINDOW capability:


Listing 2. Many implementations can be connected to an interface<br />

array member property.<br />

DEFINE CLASS WaitMsgServer AS MsgInterface<br />

FUNCTION Init<br />

*-- Load interface objects<br />

THIS.aImp[1]= CREATEOBJECT("WaitMsg")<br />

THIS.aImp[2]= CREATEOBJECT("RegularMsg")<br />

THIS.aImp[3]= CREATEOBJECT("InfoMsg")<br />

THIS.aImp[4]= CREATEOBJECT("WarningMsg")<br />

THIS.aImp[5]= CREATEOBJECT("ErrorMsg")<br />

*-- Supporting the legacy singular WaitMsg capability<br />

THIS.oImp= aImp[1]<br />

ENDFUNC<br />

FUNCTION Execute( txPassed, tnMessageType)<br />

THIS.aImp[tnMessageType].Execute( txPassed)<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS WaitMsg AS MsgImplementation<br />

FUNCTION SHOW( txPara1)<br />

WAIT WINDOW THIS.cMessage<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS RegularMsg AS MsgBoxImplementation<br />

cTitle= "My Application"<br />

ENDDEFINE<br />

DEFINE CLASS InfoMsg AS RegularMsg<br />

nIcon= 64<br />

ENDDEFINE<br />

DEFINE CLASS WarningMsg AS InfoMsg<br />

nIcon= 48<br />

ENDDEFINE<br />

DEFINE CLASS ErrorMsg AS WarningMsg<br />

nIcon= 16<br />

nButtons= 5<br />

ENDDEFINE<br />

DEFINE CLASS MsgInterface AS CUSTOM<br />

*-- Abstract message interface class<br />

DIMENSION aImp[4]<br />

aImp[1]= .NULL.<br />

aImp[2]= .NULL.<br />

aImp[3]= .NULL.<br />

aImp[4]= .NULL.<br />

*-- Virtual<br />

FUNCTION Execute( txPassed)<br />

ENDDEFINE<br />

DEFINE CLASS MsgBoxImplementation AS MsgImplementation


.<br />

Containership BRIDGE<br />

Containership implementations are similar to array member implementations. In Visual FoxPro,<br />

containership is superbly implemented, so containership BRIDGEs are easy to create and<br />

manage. Useful are the Controls() array, the PARENT keyword, SetAll(), and AMEMBERS(,,2),<br />

which make it possible to manage containership nesting.<br />

The containership relationship is illustrated in , and Listing 3 is an illustrative code example.


Listing 3. Multiple implementations instantiated in an interface container.<br />

DEFINE CLASS MsgServer AS MsgInterface<br />

FUNCTION Init<br />

*-- Load an interface object with implementations.<br />

*-- Exercise for the reader: Imagine delaying<br />

*-- the AddObject calls until actually needed.<br />

THIS.AddObject( "msgWaitWindow","WaitMsg")<br />

THIS.AddObject( "msgRegular", "RegularMsg")<br />

THIS.AddObject( "msgInfo", "InfoMsg")<br />

THIS.AddObject( "msgWarning", "WarningMsg")<br />

THIS.AddObject( "msgError", "ErrorMsg")<br />

ENDFUNC<br />

FUNCTION Execute( tnPassed, tcMessage)<br />

*? I don't recommend doing it quite like this --<br />

*? This simple example assumes you know the number<br />

*? of the implementation object. In a perfect but<br />

*? (less concise) example, .Execute(x,y) could accept<br />

*? an x of type "C", as in<br />

*? .Execute("Warning", )<br />

*?<br />

THIS.Controls(tnPassed).Execute(tcMessage)<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS WaitMsg AS MsgImplementation<br />

FUNCTION SHOW( txPara1)<br />

WAIT WINDOW THIS.cMessage<br />

ENDFUNC<br />

ENDDEFINE<br />

DEFINE CLASS RegularMsg AS MsgBoxImplementation<br />

cTitle= "My Application"<br />

ENDDEFINE<br />

DEFINE CLASS InfoMsg AS RegularMsg<br />

nIcon= 64<br />

ENDDEFINE<br />

DEFINE CLASS WarningMsg AS InfoMsg<br />

nIcon= 48<br />

ENDDEFINE<br />

DEFINE CLASS ErrorMsg AS WarningMsg<br />

nIcon= 16<br />

nButtons= 5<br />

ENDDEFINE<br />

DEFINE CLASS MsgInterface AS Container<br />

*-- Abstract message interface class<br />

*? Hardcoded dimension<br />

DIMENSION aImp[4]<br />

aImp[1]= .NULL.<br />

aImp[2]= .NULL.<br />

aImp[3]= .NULL.


What to expect from BRIDGE patterns<br />

On balance, classes built as BRIDGEs are easier to extend and adapt since their interfaces and<br />

implementations can be subclassed independently. Moreover, the BRIDGE pattern is easy to<br />

grasp and is easily communicated among developers and designers.<br />

Even though programming interfaces and implementations are separate, a high degree of<br />

cohesion (coupling) between participants is to be expected unless steps are taken to abstract their<br />

relationship. The BRIDGE pattern scales nicely, and it's not unusual to find a BRIDGE<br />

containing other BRIDGEs.<br />

This is a good rule of thumb: when creating a new class, always consider making it a BRIDGE<br />

first.<br />

Next month we'll talk further about abstracting the relationship between objects when we discuss<br />

OBSERVER and MEDIATOR patterns.<br />

Steven Black is the technical lead at SBC/UP!, a FoxPro consulting company based in Kingston, Ontario,<br />

Canada. He specializes in multilingual and multicultural FoxPro development, "out of control" project<br />

turnarounds, and specialized consulting to other developers. He is the author of Steven Black's INTL<br />

Toolkit, a framework for multilingual FoxPro development in FoxPro 2.x and 3.0, and several third-party<br />

GENSCRNX tools including TABS and the GENX help file. He recently contributed to The Hacker's<br />

Guide to Visual FoxPro (Addison Wesley, 1996) by Tamar Granor and Ted Roche. 613-542-3293,<br />

CompuServe 76200,2110, Internet steveb@stevenblack.com.<br />

Sidebar: References<br />

Black, S. (1995) Pattern Implementation in VFP, European Visual FoxPro Developers<br />

Conference '95 session notes, E-PATT, Frankfurt, Germany. And also: GLGDW '95 session<br />

notes, #30, Milwaukee, WI.<br />

Coplien, J, and Schmidt, D (1995) Pattern Languages of Program Design. Reading, MA.<br />

Addison Wesley. ISBN 0-201-60734-4.<br />

Gamma, E., Helm, R., Johnson, R, and Vlissides, J. (1994) Design Patterns, Elements of Object<br />

Oriented Software. Reading, MA. Addison Wesley. ISBN 0-201-63361-2<br />

Pree, W (1995) Design Patterns for Object Oriented Development. Reading, MA. Addison<br />

Wesley and ACM press. ISBN 0-201-42294-8.<br />

Tip: Make a Method to Modify Properties<br />

Steven Miller<br />

In Visual FoxPro, you can change the properties of an object by direct assignment, as in the


following:<br />

MyForm.Caption = "A Great Form"<br />

While this is handy, OOP purists like to create a method to change the property for<br />

encapsulation. This might seem like too much to bother with, but it's not. The ChangeProp()<br />

method takes two lines of code:<br />

LPARAMETERS lcProperty, lcValue<br />

STORE (lcValue) to ("this." + lcProperty)<br />

You can add this method to any object. When you do, you can change any property with a single<br />

line of code, just as before:<br />

MyForm.ChangeProp( "Caption", "A Great Form" )<br />

Steve Miller is a senior database developer with microMANAGEMENT Inc., a software development and<br />

computer consulting company in Troy, Michigan. Steve is a contributing author to Using Visual FoxPro 3<br />

from Que, and has contributed numerous articles to <strong>FoxTalk</strong> and FoxPro Advisor. CompuServe<br />

70632,542.<br />

Stonefield Data Dictionary and Stonefield<br />

Database Toolkit<br />

Kelly Conway<br />

Every application has a number of data-centric requirements that have to be laboriously coded and<br />

recoded each time. Why design, write, and maintain this code yourself? The Stonefield Data<br />

Dictionary for FoxPro 2.x and the Stonefield Database Toolkit for Visual FoxPro relieve you of<br />

having to write these routines. Kelly Conway investigates both.<br />

How long have you developed applications with FoxPro? How long have you wished that<br />

FoxPro contained a data dictionary? If you're like most FoxPro developers, your answers to both<br />

questions probably are about the same. A data dictionary serves at least two major purposes.<br />

First, it can serve as a repository of information about your application and its tables. Second, it<br />

provides that information to reusable functions that can then perform data-driven operations.<br />

Each of these purposes is important to developers. The information-repository aspect is<br />

important because this information can be used to produce system documentation and help new<br />

(2)


team members and maintenance programmers get up to speed on an application. The data-driven<br />

functionality is important because the reusable functions promote rapid application development<br />

while providing much-needed functionality for your applications (for example, table repair and<br />

reindexing, table look-ups).<br />

The Stonefield Data Dictionary<br />

In 1992, Stonefield Systems Group Inc. released Stonefield Data Dictionary (SDD). Originally<br />

an in-house tool to support Stonefield's own custom development efforts, SDD evolved into a<br />

commercial product that became available in 1992.SDD was the first commercial data dictionary<br />

for FoxPro and, through several version updates, remains the only standalone data dictionary for<br />

FoxPro.<br />

SDD addresses both of the purposes outlined earlier. The product is an active data dictionary that<br />

retains important system information and provides a library of reusable routines that can put the<br />

system information to effective use in a running application. Developers also may write their<br />

own data-driven routines that utilize the information in the data dictionary.<br />

Features<br />

SDD provides FoxPro developers with a long list of features that includes maintenance of<br />

detailed table information and comments, generation of system documentation, structural<br />

updates to production tables and index files, repair of corrupted table headers, other data-driven<br />

routines that you may use in your applications, and a GENSCRNX driver that promotes rapid<br />

development of screens and helps to make SDD an active data dictionary. In addition to<br />

providing these and other much-needed features that FoxPro lacks, SDD provides a consistent<br />

front-end for performing data administration functions that otherwise require the use of several<br />

separate native FoxPro tools.<br />

The SDD options are available from a menu pad named Dictionary that is added to the end of<br />

your FoxPro menu when you run the supplied INSTDICT application. The first step to using<br />

SDD (after installation) is defining your tables. SDD allows you to both create new FoxPro<br />

tables and import information for existing tables. It even provides a batch add feature that will<br />

add all tables from a particular subdirectory into the dictionary.<br />

Data administration functions are performed from within the screen that appears when you<br />

choose Data Dictionary from the Dictionary menu pad. The Maintain Data Dictionary screen<br />

(see Figure 1) provides a switchboard from which you can define information for each table and<br />

bring up other screens that allow you to create, edit, and delete table fields, index tags, relations,<br />

views, list expressions, and alternate table aliases. I'll describe each of these items.<br />

The ability to define and maintain table fields is fairly self-explanatory. But SDD adds some nice<br />

features to assist you with these chores. Each data dictionary you create contains an entry for a<br />

table named _LIBRARY. _LIBRARY isn't actually a table to be used by your application.<br />

Rather, it's a place to hold fields that you define once and then use throughout your application.<br />

For instance, you can define a __LIBRARY field named PHONE that holds all of the formatting,<br />

error, and validation information for a standard telephone number field. You can then use a copy


of that field in any table that requires a phone field. Subsequent changes to that field in<br />

_LIBRARY can be reflected in tables that contain a copy of that field by choosing the update<br />

option.<br />

With SDD, you can also give each field a 40-character description and define a standard picture<br />

clause, screen when and valid snippets, error message, status message, range, and help text for<br />

each field. Then, you can use the "Quick Screen" button to intelligently generate a screen (.SCX)<br />

that uses these settings for each field. This option uses DD.PRG, a GENSCRNX driver. Many<br />

people seem to shy away from GENSCRNX because of the time that it takes to get up and<br />

running with it. But DD does all of the GENSCRNX setup work for you so that you don't really<br />

even have to know it's there.<br />

The ability to define and maintain index tags is also self-explanatory. But SDT provides a feature<br />

that allows you to define an index tag for each table field and a tag on DELETED() with just a<br />

couple of mouse clicks. You can also use the "Update" button to update the actual table with any<br />

structural changes you've made to fields or index tags.<br />

You can define relations between tables that can be established at runtime by calling the SetRel()<br />

routine. No more need for complex sets of SET ORDER and SET RELATION commands in<br />

your application. These relations can also provide relational integrity with cascade, restrict, set<br />

null, and no action (ignore) options. All you have to do is call the CanUpd() or CanDel() routine<br />

to determine whether your user's change or delete will violate your application's relational<br />

integrity rules. Then, if appropriate, you may call the DoUpd() and DoDel() routines to enforce<br />

the rules.<br />

SDD views are similar to FoxPro view (.VUE) files. The differences are that SDD views are<br />

data-driven (for example, a change to a relation is reflected in the view), that they can be<br />

assigned 20-character names that may include spaces, and that their definitions are stored in a<br />

table rather than in separate .VUE files. If you use FoxPro view files to manage which tables are<br />

open and the relations between them, you'll want to use SDD views instead.<br />

List expressions basically are pre-defined BROWSE windows that you can call upon to give<br />

your users pick-list functionality with optional incremental searching capability (courtesy of<br />

JKEY). You define list expressions by picking fields from the selected table and any of its<br />

related tables. You then can specify the column header and width as well as the BROWSE when<br />

and valid clause for each selected field. There's a function named SetTag()that you can call from<br />

any field's when clause so that the table order is set to the appropriate tag when your users click<br />

in a particular column. On top of all that, you can use the "Test" button at design time to see how<br />

your list expressions will look to the user. When combined with the BROWSER(), PICKLIST(),<br />

and LOOK_UP() data-driven routines that SDD includes, list expressions provide an amazing<br />

range of possibilities for performing table look-ups and providing pick-lists.<br />

SDD table aliases are designed for those times when you might want to use the AGAIN clause of<br />

the FoxPro USE command. For instance, you might decide to USE ... AGAIN a look-up table so<br />

that you can perform a SEEK without having to worry about resetting the record pointer and<br />

order. By defining a table alias, you simply open (using the OpenDBF() routine) and use that


alias as if it were a separate table.<br />

Each table, field, index tag, relation, alias, and list expression can have a 40-character name that<br />

may contain spaces. Those names then are used in the data-driven routines so that your users see<br />

things like "3 rd Quarter Account Balance" and "Customer Name" rather than "ACTBAL3Q"<br />

and "CUSTNAME" in their applications.<br />

SDD can generate system documentation for your entire application or for a specific table. These<br />

reports include all of the information that is maintained for each table, index, list expression,<br />

view, and relation. The system information report makes a nice cover sheet for the system's<br />

documentation.<br />

In addition to the data-driven routines that I've already mentioned (for opening tables, updating<br />

table structures and index files, setting the index order in a list expression, performing table<br />

look-ups, and so forth), SDD provides data-driven routines to accomplish many common<br />

application tasks. For example, AUTOKEY() is a procedure that generates the next-available<br />

sequential key for the selected table. OPENFILE() opens all tables in the dictionary that are<br />

flagged to be opened automatically, optionally in a defined sequence. GETFILT is a screen that<br />

provides a mechanism for asking the user to specify filter and query conditions, using the<br />

40-character descriptions of fields and indexes. GETSORT is a screen that asks the user to<br />

specify a table sort order by choosing from a list of index tag descriptions. TEMPFILE() is a<br />

routine that generates a unique filename. This is merely a sampling of the many data-driven<br />

routines that are shipped with the product.<br />

Documentation<br />

The documentation that comes with SDD is top notch. The 164-page manual provides everything<br />

you need to learn to use SDD. It also contains an index and reference sections for the data-driven<br />

routines that come with the product. In addition, the documentation contains several sections and<br />

appendices that detail how FoxPro and SDD handle certain situations. These behind-the-scenes<br />

glimpses can help developers better understand and extend both products.<br />

But the documentation doesn't stop with the manual. The product comes with online help as well<br />

as full source code that is superbly commented. The requirements for using every routine are<br />

specified in a consistent program header that includes descriptions of any parameters and<br />

whether each is required or optional. In fact, the program documentation is so good that you'll<br />

probably find little need for the printed manual and online help after using the product long<br />

enough to become familiar with what's available.<br />

When you install SDD, it creates a DEMO subdirectory that contains a set of data dictionary<br />

files and a small application that shows how some of the data-driven routines may be used. You<br />

may find that running and examining the demonstration application is one of the best ways to<br />

quickly learn about SDD features.<br />

Support<br />

Support for Stonefield products also is superb. It's available via CompuServe, fax, and telephone.


Following the trend set by many other vendors, Stonefield recently instituted a charge policy for<br />

telephone support. Priority support is available for $199 per year. This plan, which is free for the<br />

first 60 days, includes version updates and allows you to contact Stonefield by telephone, fax, or<br />

CompuServe. Standard support is free of charge and is provided via CompuServe only. Standard<br />

support customers must pay for version updates (typically $99).<br />

When a bug is reported, it's fixed immediately and the fix is sent to the person who reported it. If<br />

it's a serious bug (one that could cause data loss, for example), an update is sent out at no charge<br />

to all registered users. If the bug is minor or occurs only under unusual circumstances, it and<br />

other bug fixes are sent out periodically as a "maintenance release," which also are at no charge.<br />

In my experience, support has been excellent both over the phone and via CompuServe. Phone<br />

calls are answered or returned quickly and CompuServe messages usually receive responses<br />

within 24 hours (often much more quickly than that). When I've reported bugs, the fix has been<br />

delivered to me via CompuServe within 48 hours. Occasionally, enhancement requests have<br />

been handled in the same way.<br />

Pricing for SDD and other Stonefield products<br />

The cross-platform (DOS and Windows) version of Stonefield Data Dictionary is $249 US, as is<br />

the Macintosh version. The product comes with royalty-free source code and a 60-day<br />

unconditional money back guarantee. One copy is required per developer; multi-developer<br />

licensing is available.<br />

Also available from Stonefield Systems Group is Stonefield AppMaker (DOS and Windows,<br />

$299, including a copy of Stonefield Reports; no Mac version yet). In a nutshell, AppMaker is an<br />

application framework/generator that uses SDD actively to promote rapid development of crossor<br />

single-platform, data-driven, and event-driven FoxPro 2.x applications, complete with<br />

security, ad-hoc reporting, and mail-merge facilities. Data Dictionary and AppMaker may be<br />

purchased together for $495. Watch for a complete review of Stonefield AppMaker in a future<br />

issue of <strong>FoxTalk</strong>.<br />

Stonefield Reports (DOS and Windows, $149; no Mac version yet) is an end-user report<br />

manager. It allows users to select and print reports through an easy-to-use user interface. Its<br />

"quick report" function allows a user to quickly create a report by simply selecting which fields<br />

to output.<br />

The bottom line<br />

SDD provides additional features that I don't have the space to cover in this review. A demo<br />

version of SDD is available in the ThirdParty Tools section of the FoxUser forum on<br />

CompuServe. Stonefield also provides a money-back guarantee, so you can see and try the<br />

features for yourself without risk. Obviously, I'm fond of Stonefield Data Dictionary. I honestly<br />

don't see why any professional FoxPro developer would choose to work without this (or at least a<br />

similar) product.<br />

Stonefield Database Toolkit


At the time this article was written, Stonefield was beta testing a new version of its data<br />

dictionary. This new version, dubbed Stonefield Database Toolkit, was developed for use with<br />

Visual FoxPro version 3.<br />

But wait a minute! Isn't a built-in data dictionary one of the features listed on the back of the<br />

VFP package? Why would anyone need a third-party data dictionary for Visual FoxPro when<br />

there's one built in?<br />

Visual FoxPro's data dictionary is no Stonefield Database Toolkit!<br />

In fact, the Visual FoxPro database container provides only a small portion of the functionality<br />

that Stonefield Data Dictionary users have enjoyed in their FoxPro 2.x development. For<br />

example, the VFP DBC doesn't contain any of the information that is needed to create or rebuild<br />

data tables or index tags.<br />

Stonefield Database Toolkit enhances the tools that Visual FoxPro provides to manage the<br />

database container and provides the ability to define extended properties for data objects and set<br />

or obtain the values of these properties at runtime. It includes a class library you can add to your<br />

applications to provide data management functions at runtime, including re-creating indexes,<br />

repairing corrupted table headers, and updating table structures at client sites.<br />

Backward compatibility with Stonefield Data Dictionary<br />

SDT includes two utilities that will help you move SDD information into VFP and SDT. The<br />

first, SDD2DBC.PRG, creates a VFP database container and tables that duplicate the<br />

data-dictionary information found in the selected SDD files. Only the SDD information that is<br />

supported by the VFP DBC is created. The second utility, MIGRATE, moves all SDD<br />

information into an equivalent VFP DBC and SDT data-dictionary combination.<br />

Almost every function that was available in SDD is also available (although most are renamed)<br />

in SDT. The SDT documentation includes a section that informs SDD users where their<br />

functions can be found in SDT. And in most cases where there is no SDT replacement for an<br />

SDD function, an equivalent code segment is given.<br />

New features in Stonefield Database Toolkit<br />

While SDD provided a place to store limited information in addition to the "standard"<br />

data-dictionary items, SDT allows an unlimited number of extended properties to be created for<br />

tables and fields. These extended properties allow you to create your own data-driven routines<br />

for just about any situation you can imagine. For example, I added an extended table property<br />

named ExportMe that allows me to specify which tables should be copied to ASCII files when<br />

the user chooses my application's Export option. Then, borrowing heavily from SDT's built-in<br />

OpenAllTables() method, I wrote an ExportAllTables() method that examines that property for<br />

each table and operates accordingly.<br />

SDT was designed to allow for the possibility that a developer might make changes to tables<br />

through the table designer or through SDT. Accordingly, changes that you make in the table<br />

designer are reflected in the extended table dictionary the next time you fire up SDT. This is in


contrast to SDD, which is unaware of changes (for example., a new index tag) that aren't made<br />

through the dictionary interface.<br />

Visual FoxPro introduced builders as a kind of RAD tool. The open architecture of VFP's builder<br />

interface provides much opportunity for third-party developers to extend VFP. SDT includes the<br />

most impressive builder I've seen. The inadequately named AutoSize builder (in fairness to Doug<br />

Hennig, I don't blame him for not calling it the ItDoesEverythingYouCouldEverWantOrImagine<br />

builder) does for VFP form development what microwaves do for cooking.<br />

VFP allows you to drag fields from the data tab of the project manager, the data environment, or<br />

the table designer onto a class or form. With SDT, you then can select the fields and run the<br />

AutoSize builder to replace the default VFP base-class controls with properly named controls<br />

that are based on the classes of your choosing. You can define both a default class for each data<br />

type and a specific class for each field in each of your tables. The builder also sizes each control<br />

to the optimal size for the field's data type and length. Optionally, a label object is created for<br />

each field, the caption of which is taken from the field's DBC caption or a cleaned-up version of<br />

the field's name. If you turn on VFP's builder lock, AutoSize will perform all of these actions on<br />

each field as you drop it onto the design surface -- even while you're moving your mouse cursor<br />

into position to grab the next field!<br />

Extending SDT<br />

One of the most exciting aspects of VFP's new object-oriented paradigm is that developers will<br />

be able to extend many e third-party tools and still incorporate version updates as they become<br />

available. For example, I added the ASCII file exporting functionality that I mentioned earlier to<br />

SDT by creating my ExportAll() method in a subclass of the SDT manager class (SDTMgr).<br />

Because I made no modifications to the original SDTMgr class, I'll simply be able to replace it<br />

with new versions that Stonefield makes available in the future.<br />

The only caveat here is that I wasn't able to give my subclass a name other than SDTMgr. This is<br />

because the class methods reference other methods and properties of the class by using the name<br />

SDTMgr. That's not a huge problem because I was able to put my subclass into a separate class<br />

library that I could name as I wished. Still, I hope that Stonefield modifies their referencing so<br />

that this problem doesn't appear in the release version or at least in a subsequent update.<br />

CodeBook and DBCX compatibility<br />

SDT is designed to be CodeBook 3.0 compatible. I beta tested SDT while developing my first<br />

few VFP applications that are based on the CB3 application framework and found no<br />

incompatibility problems between the two products. In fact, SDT provides some features that fit<br />

nicely into the CodeBook framework, enhancing both the development of a CB3 application and<br />

the application itself.<br />

The AutoSize builder that comes with SDT makes child's play of the task of creating CB3<br />

business objects. You simply tell VFP that you want to create a new class based on the CB3<br />

business object class, drag and drop the appropriate fields, select the fields, and run the builder.<br />

You can specify the CB3 classes to use in AUTOSIZE.DBF, and you can also specify CB3's or


your own custom control classes to be used for specific fields (for example, the intelligent<br />

cDateTextBox for date fields).<br />

Other SDT routines and classes may be added to the menu of a CB3 application to add new or<br />

replace existing functionality. For example, by pointing the standard CB3 Database Utilities<br />

menu bar to the Reindex() method of the SDTMgr class, you can incorporate SDT functionality.<br />

You could also add new options to your CB3 application that would invoke other SDT methods<br />

such as the Update() method that would allow your users to ensure that their table and index<br />

structures are correct after installing a new set of dictionary files.<br />

The SDT manager class is a subclass of the DBCX manager class. That means that as DBCX is<br />

enhanced and upgraded, SDT users will be able to take advantage of those new and improved<br />

features.<br />

Stonefield Database Toolkit pricing and availability<br />

Stonefield has released SDT at $249 US. Stonefield Data Dictionary owners can purchase an<br />

SDT upgrade for $149.<br />

Kelly Conway has developed FoxPro applications for eight years. Based in Lee's Summit, Missouri. He<br />

works as in-house developer for Dimoco Manufacturing Company. Kelly is a cofounder and past<br />

president of The Midwest Fox Pros -- a four-year old FoxPro user group that serves 100-plus members<br />

from throughout the heartland.<br />

Read and Write Your Own .INI Files Inside<br />

FoxPro with SetIni() and GetIni()<br />

Whil Hentzen (3)<br />

With the release of Visual FoxPro and Windows 95, the preferred configuration file methodology is<br />

to use the Windows registry. However, this isn't always an option and you may wish to fall back on<br />

the .INI file mechanism. This pair of FoxPro 2.x functions will painlessly read and write settings to<br />

standard .INI text files.<br />

With the advent of Windows 3.1 at the beginning of this decade (yes, it's been that long!),<br />

developers started mimicking the use of .INI text files for the use of configuring an application.<br />

This technique had several advantages. First, it was easy to make changes to a text file at the site<br />

of the application's installation; a task not quite as easy if you kept the configuration information<br />

in a .DBF and the application was running off of a runtime version of FoxPro. Second, to an<br />

experienced user or system administrator, the .INI file concept was familiar, so you could direct<br />

them to make changes remotely. And third, .INI files were platform independent -- they worked<br />

equally well with DOS, Windows, Mac, and UNIX.


As Microsoft shifts its weight behind Windows and continues to encourage the use of OLE, the<br />

preferred method of storing configuration data is through the Windows Registry file. However,<br />

as we've seen, this isn't always going to work. If you're still working on non-Windows versions<br />

of applications, the registry isn't accessible. And you may find that you're uncomfortable with the<br />

use of this new beast and would prefer to stick with a mechanism that's more familiar.<br />

Still, while the concept of reading and writing to a text file to store the application's settings is<br />

simple, the work of writing the code that will manage the creation of the file, the creation and<br />

updating of sections inside the .INI file, and the handling of the various values under each<br />

section -- well, it's pretty close to busy work. There are more interesting things you could do<br />

with your time.<br />

Fortunately, Felix H. Gonzalez has written a pair of functions that you can just plug into your<br />

applications that take care of this for you. Spend 30 seconds with SetIni() and GetIni(), and<br />

you've just saved yourself several hours of drudgery. These functions aren't rocket science, but<br />

saving the better part of a day is pretty cool too, wouldn't you say?<br />

How to use SetIni() and GetIni()<br />

An .INI file has the following format:<br />

;MUPPET.INI<br />

[Miss Piggy's Parameters]<br />

Kermit = Boyfriend<br />

Ralph The Dog = My Best Pal<br />

Any line beginning with a semicolon is ignored, the section heading is delimited with brackets,<br />

and the variables for that section are separated from their values with an equals sign. SetIni() and<br />

GetIni() allow you to maintain both the section heading and the variables and their values.<br />

As you've noticed, I particularly like Cool Tools that you can use without reading the<br />

documentation, or, if you have to read the instructions, it takes only a minute to do so. The<br />

SetIni() and GetIni() functions meet this criteria. They use the same syntax:<br />

m.lWasSuccessful = SetIni( "INI file name", ;<br />

"Section Heading", "Variable Name", "Value")<br />

m.cReturnValue = GetIni( "INI file name", ;<br />

"Section Heading", "Variable Name")<br />

SetIni() returns a logical true or false, depending on whether the value was able to be written.<br />

I've only found one case where the function fails: when the .INI to be written is marked as<br />

read-only. GetIni() doesn't require a fourth parameter since it's purpose is to return the value of a<br />

variable in the .INI file. The "fourth parameter" is actually the return value:


m.lWorked = SetIni("muppet", "Miss Piggy's Parameters",;<br />

"Ralph The Dog", "My Best Pal")<br />

? m.lWorked<br />

.T.<br />

m.cRetVal = GetIni("muppet", "Miss Piggy's Parameters", "Kermit")<br />

? m.cRetVal<br />

Boyfriend<br />

As you can tell from these two function calls, using them is completely painless. If the .INI file<br />

doesn't exist, it's created; if it does exist, the current one is saved as a .BAK file and a new<br />

version is created. If you don't include an .INI extension, it will be created for you. If you don't<br />

include a path, the .INI file is assumed to be (or is created) in the current directory.<br />

SetIni() will create section and variable names if they don't exist. GetIni() will return an empty<br />

string if the section or variable doesn't exist. The section and variable names are case sensitive,<br />

but you can include spaces and other delimiters in both.<br />

I love stuff like this -- saves me a bunch of time and is easy to use. How could it get any better?<br />

Where to find SetIni() and GetIni()<br />

Both functions are included in download file _CLTOOL.EXE. You can also find them in Library<br />

13 of CompuServe's FoxForum. They're freeware; use them as you wish. Just the .FXPs are<br />

provided; the source code is $12, and you can contact the author, Felix H Gonzalez at<br />

CompuServe 75404,1716 for details. However, in my experience, even if you don't want the<br />

source code, be sure to drop him a note telling him that you like his work.<br />

Whil Hentzen is president of Hentzenwerke, a Milwaukee, Wisconsin, software development firm that<br />

specializes in strategic FoxPro-based business applications for Fortune 500 companies. He is also the<br />

editor of <strong>FoxTalk</strong>. 414-224-7654, fax 414-224-7650, CompuServe 70651,2270.<br />

Sidebar: Book of the Month<br />

The last few months, I covered a series of books that can help you become a better programmer.<br />

This month, the book will help you become a better manager of programmers. This advice is<br />

indispensable even if the only programmer you manage is yourself.<br />

"Peopleware" by Tom DeMarco and Timothy Lister (Dorset House Publishing, ISBN<br />

0-932633-05-6) is one of the industry's classic tomes. The entire book can be summed by the<br />

closing sentences of Chapter 6 (intriguingly titled "Laetrile"). The chapter discussed the futile<br />

search for the "Silver Bullet" that all software managers hope for -- the magic tool that will<br />

double their department's productivity overnight. But the answer isn't in technology; it's in the<br />

application of more effective ways of handling people, modifying the workplace, and changing


the corporate culture. "Sharon knew what all good instinctive managers know: The manager's<br />

function is not to make people work, but to make it possible for people to work."<br />

Peopleware is a quick read -- 190 pages of large type in a oversized paperback, but the<br />

hypothesis and the answers ring true in a way that 10,000 pages of research printed in six-point<br />

type couldn't provide. The length and writing style just make the story that DeMarco and Lister<br />

tell immediately accessible.<br />

Their fundamental theory is that people want to do a better job, if only the company, rules, and<br />

environment would let them. Supposing this, they then set out to design the office environment,<br />

selecting the right people, and creating productive teams so that software developers can do what<br />

they want to do: design, code, and implement high-quality software.<br />

One of the first things that strikes you when you walk through the halls of RDI Software<br />

Technologies in Chicago is that virtually everyone in the company has a private office with a<br />

door that closes. Study after study has shown that the single most important factor in<br />

programmer productivity is long periods of uninterrupted time. How can that factor evaluate to<br />

anything but a negative number when the workplace consists of a bullpen with five foot cubicles<br />

and a company paging system that blares out unintelligible nonsense every 47 seconds? But your<br />

company wouldn't allow it? DeMarco and Lister detail example after example of how to "skirt<br />

the system" so that, at the very least, your most important project teams get into an environment<br />

that works for, not against, their goals.<br />

What's another issue in creating a high value of what the authors call the Environmental (or "E")<br />

Factor: Uninterrupted hours/Body-Present Hours and the ability to silence that miracle of<br />

modern engineering, the telephone. They relate the tale of the manager who rebuked an entire<br />

department of highly paid software engineers: "You guys have to answer your own phones. If<br />

you all forward your phones to the secretaries, they'll never get any work done!" At my<br />

company, we've taken this idea to heart and now have "telephone free zones" each morning and<br />

each afternoon. The longest a customer will wait for a call to be returned is two hours -- should<br />

they call at the very beginning of one of the zones -- but they have four hours in the day where<br />

they can get through directly, and the resulting effect of having four hours of uninterrupted<br />

programming is a significant increase in our productivity -- and morale!<br />

If these ideas are of interest to you, then you'll want to pick up Peopleware because I don't have<br />

space to go through the other 98 excellent suggestions.<br />

Dare to Dance the Tide<br />

Les Pinter<br />

Occasionally, a voice asks you what you're going to do with the rest of your life. It asks you to<br />

make a decision -- often a hard one. Sometimes it gives you plenty of warning. Sometimes it<br />

pops up at the most awkward moments. Sometimes you don't hear it until it's too late.<br />

We're faced with decisions every day. Do we stop making money for a few days while we learn


FoxExpress, FoxFire!, Intl, Stonefield's AppMaker, or even the new OLE controls? It might be<br />

worth it, but it might be a waste of time. Do we dare risk it?<br />

We see others doing what we're doing for twice the pay. Our employers treat us with disdain,<br />

testing us constantly with insults. If we'll tolerate being abused, we'll certainly tolerate being<br />

underpaid. Pride, or the lack of it, is like that. Do we let it continue, or put a stop to it.<br />

The most valuable advice I ever received was a single sentence. A woman I was having dinner<br />

with, tired of hearing me lament my fate, stood up and said, "Les, if you don't like your life,<br />

change it." Then she left. I sat there for the longest time, trying to make sense of those nine little<br />

words. It wasn't that easy; she didn't understand.<br />

Well, the one who didn't understand was me. It's not supposed to be easy. If it were easy,<br />

everyone would do it. The entire pleasure of life is that it's hard so that we can be proud of<br />

ourselves when we overcome adversity. And all it takes is to say enough's enough.<br />

The next three steps are always the same:<br />

1. Figure out what you want<br />

2. Find out what it takes to get there<br />

3. Do what has to be done<br />

I wanted to do a FoxPro seminar series: That's step one. Step two is to write the course materials<br />

and work out a schedule, and step three is to place an ad. It's expensive, and you don't find out<br />

whether the first ad worked until you've paid for the second ad. You pays your money and you<br />

takes your chances.<br />

I've wanted to write a novel for years. So why hadn't I written a novel? Partly because I didn't<br />

have a story to tell. Life gave me one. Once I figured out what the story was -- and I only have<br />

one -- it was easy. The second step was to lay out the story, and the third step was to start<br />

writing. The Valley will be done this spring. I'm a little concerned about what some of the<br />

raunchy passages may do to my image, but hey: You pays your money and you takes your<br />

chances.<br />

You want to be an independent consultant: That's step one. Step two is to get as good as you can,<br />

and let people know about it. Step three is to quit your job. A friend of mine in Las Vegas waited<br />

a year to do this. I gave him moral support, but the hard part -- making the decision -- was his.<br />

Finally, he cut the cord. Within a week, he was booked solid. He still is, more than a year later.<br />

If you follow your dream, you may be sorry. But if you don't, you will be sorry.<br />

While I'm finishing my novel, I play a Garth Brooks CD -- one song in particular -- over and<br />

over. It's called The River. This is the stanza that sticks in my mind:<br />

Too many times we stand aside and let the water slip away,<br />

`Til what we've put off `til tomorrow


has now become today;<br />

So don't you stand upon the shoreline<br />

and say you're satisfied;<br />

Choose to chance the rapids, and dare to dance the tide.<br />

Les Pinter publishes the Pinter FoxPro Letter in the United States and Russia. 916-582-0595,<br />

CompuServe, 76434,104.<br />

Fox World News<br />

Micromega Systems Inc. recently announced four major developments: they've started to ship<br />

the Foxfire! Query Tool and Report Writer version 3.0, a major new release; they've made<br />

available several products and services related to Foxfire! 3.0 and Foxfire! Report Writer for<br />

SBT has been released.<br />

Foxfire! 3.0 began shipping on February 1. The new version incorporates many significant new<br />

features including outer join support, top n values, WWW-browseable reports, independent<br />

access to the filter-builder from other programs, and DDE output to Excel (for worksheets,<br />

graphs, and pivot tables).<br />

One of the most important developer benefits of version 3.0 is that it's engineered to run with<br />

both FoxPro 2.x and Visual FoxPro. The new release consists of four program variations, each<br />

individually engineered for FoxPro 2.x for Windows, DOS, Macintosh, and Visual FoxPro 3.0<br />

for Windows. Each program can share data dictionaries and reports. Each platform is sold<br />

separately, but the Windows product includes both FoxPro 2.x and VFP executables at no extra<br />

cost. Foxfire! v3.0 for Visual FoxPro takes advantage of VFP's new database engine features,<br />

including the database container architecture (DBC). It's also meta-data compatible with the<br />

Visual FoxPro 3.0 Codebook (by Flash Creative Management) and other Codebook-compatible<br />

products through the DBCX compatibility standard (which is being developed jointly by several<br />

third party product vendors including Micromega).<br />

Micromega has begun offering the Foxfire! System Administrator's Kit, a copy of the<br />

Foxfire! source code, Reference Manual, and two User Guides intended for developers who<br />

want to keep source code at each customer site. A special abridged edition of the full Reference<br />

Manual provides complete training and reference for Foxfire! end-users ($25 each). Foxfire! 3.0<br />

Docs on Disk includes on-disk versions of the both the Reference Manual and User Guide<br />

($395). In addition regional Foxfire! Training End User and Developer classes will be held in<br />

several major U.S. cities (call Micromega Training at 415-346-4401 for information).


1 (Popup - March 1996)<br />

_egger.exe<br />

_conway.exe<br />

_cltool.exe<br />

2 (Popup - March 1996)<br />

_egger.exe<br />

_conway.exe<br />

_cltool.exe<br />

3 (Popup - March 1996)<br />

_egger.exe<br />

_conway.exe<br />

_cltool.exe<br />

Endnotes

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!