Special Sneak Preview!


HACKER'S GUIDE™ TO
VISUAL FOXPRO 3.0

An Irreverent Look at
How FoxPro Really Works!

by TAMAR E. GRANOR and TED ROCHE


Are you serious about working in Visual FoxPro? Do you want to really put FoxPro through its paces, but find yourself frustrated by commands that don't work the way you expect or documentation that seems incomplete? Would you rather fight with this wonderful program than switch? If so, the Hacker's Guide to Visual FoxPro 3.0 is for you.

In this sneak preview, we've tried to give you a hands-on look at everything the book has to offer. Check out the (offbeat!) writing style, test the reference entries against your own experiences with VFP, or download some of Tamar and Ted's sample code and put FoxPro through its paces. Here's what's included:

For more information on how to get your hands on a copy, see Addison-Wesley's How to Order page. This page includes a list of bookstores that carry A-W Developers Press titles.


Overview of the Hacker's Guide to Visual FoxPro 3.0

Hacker's Guide to Visual FoxPro 3.0 is the definitive reference to how all the commands, functions, properties, events, and methods in Visual FoxPro 3.0 really work. FoxPro experts Tamar Granor and Ted Roche show you which commands to use, which to avoid, and which ones don't work quite the way the manual says they do. You'll learn about common and not-so-common bugs and how to work around them, plus you get an introduction to OOP, SQL, and Client-Server, as well as productivity hints, tips for creating your own Builders and Wizards, and advanced coverage of OLE and OLE Automation. If you want to make your FoxPro programs scream (and not vice versa) then the Hacker's Guide to Visual FoxPro is for you.

The book is divided into five sections. The first, "Wow! What a Concept," is an overview of Visual FoxPro, organized by the various components of the language: Xbase traditions and assumptions, SQL, OOP, data structures, and more.

The second section, "These Are Not Your Father's Power Tools," takes a high-level view of application development and the Power Tools, including some of the authors' favorite tips and tricks for working with the tools.

Section 3, "Franz and Other Lists," is a nearly random assortment of lists, running the gamut from hardware suggestions to FAQs to things that sure feel like bugs when you run into them to ideas about debugging to optimization tips.

After you finish with all these appetizers, Section 4 is the main course. It's a complete reference to Visual FoxPro's commands, functions, properties, events, methods, and system variables--even a few operators like "%" and "&", just for the heck of it.

Finally, Section 5 is for those daring souls who want to take the product a little further. It covers the tools for building your own Builders and Wizards (something we think a lot of folks will want to do) and talks about the relationship between Visual FoxPro and OLE 2 (kind of like the one between you and your mother-in-law).

BACK TO THE TOP


Who Needs This Book?

(From the Preface by Tamar Granor and Ted Roche)

We love Visual FoxPro. We'd rather use it to develop applications than any other product we've ever worked with.

But (isn't there always a "but"?) Visual FoxPro's not perfect. Some parts of the language don't work as they should. Other do what they're supposed to do, but we can't imagine why anyone thinks you'd want to do that. And some pieces do exactly what they're supposed to do and it's a good thing to do but it's hard as heck to understand. We should add that the Visual FoxPro documentation is good--in fact, it's the best FoxPro documentation to date--but there still are too many places where it's wrong or incomplete.

Enter this book. This is the book for the times when you've done it just as the manual shows, it still doesn't work, and you're running out of hair to pull out. It's for those days when you think you've found a command that does what you need, but you can't make any sense of what Help has to say about it. We'll tell you how it really works or why you shouldn't do it the way the manual shows or that it doesn't work at all, and show you how to do it another way.

What This Book is Not

This is an intermediate-level book. This book is not a replacement for the online help or the language reference manual. It's a supplement. If something works the way it's documented, we won't repeat all the gory details. Instead, we'll summarize that and tell you anything the manual doesn't tell you.

On the other hand, if a command or function or property or event or method doesn't work as documented or if the documentation doesn't make sense, we dig down and explain it in detail. And, of course, in those cases, we still tell you the stuff the manual doesn't say.

This book is also not the way to learn Visual FoxPro in the first place. There are some terrific books out there designed to teach you Visual FoxPro. If you're new to Visual FoxPro, get one of those and work your way through it. Then get this book to help you move on.

This book is also not an advanced application framework. There are several good commercial frameworks available that will provide you with all the code you need to start plugging your information into their systems and getting working apps out the other end. We like and work with several of the frameworks, and we do not intend to duplicate their work here. Instead, we try to provide an advanced reference when you need to step outside the framework's box, or need to troubleshoot something the framework isn't doing right. You may also use this book to develop your own framework, but that exercise, as our profs loved to say, is left to the student.

We assume you're already familiar with the basics of Visual FoxPro, that you know how to use the Power Tools, that you've spent some time with the product. While we cover some introductory material, it's not at the level you want if you are a beginner.

So Who Does Need This Book?

Immodestly, we say anyone who's serious about working in Visual FoxPro needs this book at his or her side. Once you've gotten past the "Help, I don't know where to begin" stage and spent some time working with Visual FoxPro, you're ready. As soon as you've spent a long day and a sleepless night fighting one of the less-than-intuitive behaviors or trying to make something work that just plain doesn't, you're probably well past ready.

In the course of the year in which this book and Visual FoxPro moved from dreams (occasionally nightmares) to reality, we've learned a tremendous amount about what's going on under the hood. This book will be sitting by our sides at our respective desks. We hope you find reading it as informative as we have found writing it.

BACK TO THE TOP


Table of Contents

Foreword
Acknowledgments
Who Needs This Book?
How to Use This Book

SECTION 1: Wow, What a Concept!

SECTION 2: These are Not Your Father's Power Tools

SECTION 3: Franz and Other Lists

SECTION 4: Visual FoxPro Reference

SECTION 5: But Wait, There's More!

Back o' da Book

BACK TO THE TOP


About the Authors

Tamar E. Granor is Editor of FoxPro Advisor magazine. She is a frequent speaker at FoxPro conferences and a consultant specializing in FoxPro.

Ted Roche is Director of Development at Blackstone, Incorporated, a Microsoft Certified Professional, author, and international lecturer specializing in FoxPro.

Tamar and Ted write FoxPro Advisor's "Ask Advisor" column and are both winners of the Microsoft Support "Most Valuable Professional" award for their contributions to CompuServe's FoxPro forums.

BACK TO THE TOP


OOP is Not an Accident!

The poetical language of an age should be the current language heightened, to any degree heightened and unlike itself, but not . . . an obsolete one.
-- Gerard Manley Hopkins, 1879

We've been hearing about object-oriented programming (OOP) for about 20 years. At first it was just theory. Then we started hearing about this language called SmallTalk. In the last few years, OOP's been everywhere, and the claims of who has it and how it will revolutionize programming have grown louder and louder.

So what is OOP? Why is it important? And will it really change the way we program?

Object-oriented programming is a way of looking at programming that's different from the procedural approach found in traditional programming languages. (It's different from the give-me-what-I-want-I-don't-care-how approach of SQL too.) Conventional programming languages look at the world in terms of actions. First you figure out what to do, then you figure out what to act on. OOP looks at the world in terms of objects: first you figure out what objects you want, then you figure out what actions the objects should take.

Consider the FoxPro 2.x way of maximizing a window: ZOOM WINDOW MyWindow MAX. You start out by indicating the action you want to take (ZOOM WINDOW), then indicate what object to apply the action to (MyWindow).

The OOP equivalent is MyWindow.WindowState = 2. You start out by indicating that you want to deal with MyWindow, then you narrow it down to MyWindow's WindowState. Finally, you indicate what you're doing--setting that state to 2 (which is maximized).

This may seem like only a minor syntactic difference, but in fact, we're really turning the whole process on its head. The hidden difference here is the change from passive elements to active elements. In procedural code, windows, controls, and so on are all passive things that we act on with commands. In OOP, windows, controls and so on are active objects that know how to do things--we simply tell them what we want them to do. (See the upcoming section The Message Is the Medium for more on this.)

The Object of My Affections

Nothing can have value without being an object of utility.
--Karl Marx, Capital

Not surprisingly, the basic unit in OOP is an object. An object is an entity that has some characteristics and knows how to do some things. For example, a form is an object. So is a checkbox or a grid.

The formal name for the object's characteristics is properties. The official name for the things it knows how to do is methods. (There are some special methods called events that react to user and system actions. See A Gala Event below for more on this subject.)

Object-orientation is really packaging. We put the properties together with the methods so that the object is self-contained. To use the object, you don't need to depend on anything outside the object itself. This essential feature of object-orientation is called encapsulation--that's one of several rather intimidating words you'll find floating around the OOP world.

Encapsulation is really pretty simple. Instead of putting data in one place and code that operates on it in another (the traditional procedural division), you package them together, so that when you have one, you also have the other. If you've ever worked with abstract data types, you're familiar with this concept. If not, we suspect it'll grow on you pretty quickly.

Don't confuse the data that's encapsulated in objects with your database data. Although there's talk about object-oriented database management systems, we're not dealing with those here. The properties of an object are its data; the methods are the code.

A Class Act

To be an Englishman is to belong to the most exclusive class there is.
--Ogden Nash

Where do objects come from? Well, there's the Mama object and the Papa object and they get together with the birds and the bees and nature takes its course. Oops, wrong kind of object.

So where do objects come from? We don't just pull them out of thin air. Instead, we base each object on a class. The class forms a blueprint or template for the object and we can create as many objects as we'd like, based on a single class. (More on where these class things come from below.)

The class definition determines what properties and methods an object has. Every object based on a particular class has the same set of properties and methods. What distinguishes one object from another is the values of those properties. (For example, one checkbox might have its caption to the left while another has the caption to the right. All that's been changed is the value of the Alignment property.)

Visual FoxPro provides two ways of defining classes: in code and through the Visual Class Designer. (Actually, there's a third way--using the Form Designer--but that's really a variation on using the Class Designer.) In either case, though, you do the same kinds of things. Specify the properties and methods of the class, and indicate initial values for the properties.

The act of creating an object based on a class is called instantiation and the object created is called an instance of the class.

That's Quite an Inheritance

I would rather make my name than inherit it.
--William Makepeace Thackeray

Suppose you have a class that's almost what you want for some purpose, but not quite. The procedural thing to do is to copy the class and make the changes you need.

But then you're stuck with two different classes to maintain. What if you find a bug in the original? You have to remember to change not just the original, but the copy that you modified. And what if you then need another class a little different than either the original or the modified version? You copy and change it again. Now you've got three things to maintain. And it keeps getting worse and worse.

One of the most powerful features of object-orientation is the ability to create subclasses of existing classes. The subclass inherits the properties and methods of the original, but allows you to make changes. Most important of all, changes to the original class are inherited by the subclass.

Inheritance is the second of the three characteristics a language must have to be considered object-oriented. (For those who care, it's also the one missing from Visual Basic. VB is object-based, not object-oriented.)

So where do all these classes come from anyway? No, not Mama and Papa class--you've been dozing through our explanation. All classes, ultimately, are descendants of one of the base classes built into the language. In the case of Visual FoxPro, we've been supplied with a rich set of 32 base classes from which all of our classes are built. More on these a little later.

Our favorite example of inheritance is pretty simple. Say you work for LargeCo, a large corporation and there's a corporate standard for input forms. The standard includes the corporate logo as wallpaper. Then, LargeCo is gobbled up by EvenLarger corporation, which declares that all forms must have its corporate logo as wallpaper.

In FoxPro 2.x, you might have handled the original requirement by saving a screen containing just the logo and copying it as the basis for all your new screens. Works great until EvenLarger comes along - then, you have to go back to every screen you've created and change the wallpaper.

Okay, how does this work in Visual FoxPro? You start off by subclassing VFP's base form class. In your subclass, you set up the wallpaper with LargeCo's corporate logo. Now, whenever you need a new form, you subclass it from your corporate form and start building. Doesn't seem so different from what you did in FoxPro 2.x.

But here's the payoff. Along comes EvenLarger--what do you have to do? You go back to your original subclass (the one based on VFP's form class). You change the wallpaper to the new corporate logo and voila! All your other forms inherit the change. That's right--with inheritance, you make the change once!

So are you sold yet? We are.

Inheritance is actually even more powerful. Not only does a subclass inherit the properties and methods of the class it's based on, it also inherits the code that's in the methods. You can override that code by specifying different code for the same method of the subclass. If you don't override, it's as if you'd put the inherited code right in the subclass's method. (See the upcoming section Hierarchies and Lower-archies for how to have the best of both worlds.)

Polymorphism is Mighty Morphism

There's one more key feature of object-orientation and it's name is even more obscure than encapsulation or inheritance. This one is polymorphism.

Actually, though, it's pretty simple. It means that different objects can have methods with the same name. The methods don't have to behave the same way (though it's a good idea for them to do similar things).

In other words, you no longer have to struggle to come up with unique names for minor variations on a theme. Every object can have a Print method--no more PrintInv, PrintCust, PrintThis, PrintThat. Just issue SomeObject.Print and that object gets printed.

The Message is the Medium

Okay, we've defined all the buzzwords and talked about objects and classes, but how does all this fit together? The key is in message passing. No, not the like the kind that got you in trouble with your second grade teacher. Well, maybe like that, actually.

The basic idea in OOP is that objects know how to take care of themselves. They contain all their data and a set of actions to perform on that data. But sometimes, they need something from another object to get the job done or they have information that another object needs. So they send a message to the other object, asking that object to do something, asking for information from that object, or telling the other object something important. These correspond roughly to invoking a method of another object, checking a property value from another object, and changing a property value in another object.

You can access a property of any object by giving the object's name, then a ".", then the property name. For example, the Picture property (which provides the wallpaper) of a form called MyForm is referenced with:

MyForm.Picture

To change a property, simply assign it a new value. To give MyForm the Fox bitmap for wallpaper, you'd write something like:

MyForm.Picture = "F:\VFP\Fox.BMP"

To store the current value of MyForm's current picture property in a memory variable, perhaps so it could be changed and later restored, you'd write something like:

cCurPict = MyForm.Picture

You reference methods similarly. Use the object name, a "." and the method name. If the method accepts parameters, enclose them in parentheses. Parentheses are optional if you're not passing parameters, but we recommend always using them when calling methods. To call MyForm's Refresh method, for example, you can write:

MyForm.Refresh

or

MyForm.Refresh()

We like the second form better because it makes it clear Refresh is a method.

The Protection Racket

Woman must not depend upon the protection of man, but must be taught to protect herself.
--Susan B. Anthony

Some objects have properties or methods that can be dangerous in the wrong hands. The way you prevent these dangers is by marking those properties and methods as protected. Protected properties and methods can be accessed only by methods of the same object.

If other objects need access to the value of a protected property, you can provide a method (not protected) whose sole function is to return that value.

For example, the disk for this book contains a class designed to keep track of connections to remote servers. That class uses an array to contain the connection information and has a property indicating how many connections it's currently tracking. Letting the outside world touch that counter property would be suicidal for this class. Instead, that property is protected and there's a method Count that returns the current connection count.

Some OOP theorists believe that all access to properties should come through methods, that no object should be able to directly read or change the properties of another. Visual FoxPro does not follow that philosophy by default, but you can design your classes to do so, if you wish.

Hierarchies and Lower-archies

In a hierarchy, every employee tends to rise to his level of incompetence.
--Laurence J. Peter, The Peter Principle, 1969

One of the most confusing aspects of object-oriented programming is that there are two different hierarchies at work. We mentioned above that you can create subclasses based on existing classes. In fact, you can do this over and over again, building a tree (the computer science kind of tree, not the nature kind of tree) of classes of as many levels as you'd like.

At the top of this tree, known as the class hierarchy, are Visual FoxPro's base classes--more on those a little further along. The next level contains subclasses derived directly from base classes. At the next level are subclasses derived from the subclasses one level up. And so on and so forth.

The reasonably standard OOP term for the class one level up the hierarchy is superclass. For reasons we can't comprehend, Visual FoxPro instead calls the class one level up the hierarchy the parentclass. Reminds us of the old joke, How many Microsofties does it take to change a lightbulb? Answer: None, they just declare darkness the new standard.

Inheritance applies to the class hierarchy. An object based on a class at the bottom of the tree has properties and methods specified for that class, plus it inherits any properties and methods of its parentclass and those of the parentclass of its parentclass (you might call it the grandparentclass) and so on all the way back to the root of the tree.

When a method of an object is called, the class the object is based on is checked. If it has code for that method, the code is executed. If there's no code there, we start climbing the class hierarchy, looking for an ancestor of this class that has code for the specified method. As soon as we find some code for that method, we execute it and stop. But until we find it, we keep climbing the tree until we reach the Visual FoxPro base class the object is ultimately derived from. Even if no code is specified anywhere on the tree, if the base class has some default behavior (like re-drawing the object upon invocation of the Refresh() method) that behavior will occur.

Contain Yourself

Now what about the other hierarchy we mentioned? This comes from the fact that one object can contain another. For example, a form is an object (based on the Visual FoxPro Form base class). A form usually contains all kinds of other objects like textboxes, checkboxes, grids, and so forth. Some of the objects in a form can contain other objects. For example, a grid contains columns, which in turn can contain headers and controls.

So the second hierarchy is the containment hierarchy. This is the map of what's inside of what. The most important point about the containment hierarchy is that inheritance has nothing to do with it at all. Objects do not inherit anything from their containers or from the objects they contain.

The second most important thing about the containment hierarchy is that the term parent is used here too. (This is one reason we're frustrated by Microsoft's choice of parentclass over superclass.) The parent of an object is the object that contains it. For example, the parent of a column is the grid containing that column. The parent of a textbox might be a form. Don't confuse parent with parentclass--they really are two different things.

One other terminology note: the objects inside another object are called members of the containing object.

Climbing Trees

At various times, we need to climb each of the hierarchies. Let's start with the class hierarchy. Say you're defining a subclass and, for some method, you want to do everything the parentclass does, but then do a few more things. Your first instinct might be to copy all the code from that method of the parentclass into the corresponding method of the new subclass. Why shouldn't you do this?

What happens if you have to change the parentclass' behavior? If you've done cut-and-paste to the subclass, you're out of luck. The changes aren't inherited.

So what should you do? Call the parentclass' method explicitly from the subclass' method. It takes a notational trick to let you do this, since you can normally only call methods of objects, not of classes. A special operator :: lets you call up the class hierarchy. The notation is:

ClassName::Method

The :: operator lets you have your cake and eat it, too. A subclass's method can call the same method of its parentclass, then do some more work. Or, if you prefer, do the extra stuff first, then call the parentclass's method. Or both: do something, call the parentclass's method, then do something else.

Moving around the container hierarchy is actually a lot more common. To send a message from one object to another, you have to be able to find the recipient. You can do that by walking down the container hierarchy until you reach the object you want. For example to refer to a spinner named spnDays on a page called pagPage1 of a pageframe called pgfCalendar of a form called frmWhoKnows, you write:

frmWhoKnows.pgfCalendar.pagPage1.spnDays

What a mouthful!

You want class definitions to be as reusable as possible. Since you may create many instances of a single class, you don't know when you're writing code what the name of the actual object will be. You also may not know what the parent of an object is. For example, a textbox might be contained by a form, a column or a page (of a pageframe).

A special operand, THIS, lets you refer to the current object without knowing its name. The PARENT operator lets you move one level up the container hierarchy without knowing what's up there. For example, to find the name of the parent of the current object, you'd write:

THIS.PARENT.Name

You can use PARENT repeatedly to climb multiple levels:

THIS.PARENT.PARENT.PARENT.Left

gives you the left edge of the object three levels up in the hierarchy. If THIS were a checkbox in a column of a grid on a page of a pageframe, that expression would refer to the page's Left property.

Since you don't always know how deep in the hierarchy you'll be, THIS has two cousins, THISFORM and THISFORMSET, which let you jump quickly to the top of the container hierarchy. Then you can climb back down a level at a time. Say, you want to address the button cmdSave that's on the current form. You can reference it with:

THISFORM.cmdSave

without worrying about where you are now on the form.

Base Clef

All fantasy should have a solid base in reality.
--Sir Max Beerbohm, Zuleika Dobson, 1911

Visual FoxPro comes with a set of built-in classes known as the base classes. FoxPro's base classes cannot be changed, but most of them can be subclassed. In fact, we recommend that one of the first things you do is subclass all the input controls and build your own forms and form classes from your subclassed controls, rather than from FoxPro's base class controls. We suggest you do this even if you change not one thing about the control because some day, you're going to want to make changes. If you've used the base classes in your forms, there'll be a lot of work ahead.

There are several ways to break the Visual FoxPro base classes into groups. The biggest division seems to be between containers and noncontainers. Containers can hold other objects while noncontainers can't--simple enough. There's also the question of whether a class can be subclassed in the Class Designer. Finally, some classes are visible while others aren't. The table below shows all 32 of Visual FoxPro's base classes and classifies them according to all three criteria.

BASE CLASS            CONTAINER?   SUBCLASS-ABLE IN CD?   VISIBLE?
CheckBox No Yes Yes
Column Yes No Yes
CommandButton No Yes Yes
CommandGroup Yes Yes Yes
ComboBox No Yes Yes
Container Yes Yes Yes
Control Yes Yes Yes
Cursor No No No
Custom Yes Yes No
DataEnvironment Yes No No
EditBox No Yes Yes
Form Yes Yes Yes
FormSet Yes Yes Yes
Grid Yes Yes Yes
Header No No Yes
Image No Yes Yes
Label No Yes Yes
Line No Yes Yes
ListBox No Yes Yes
OLEBoundControl No Yes Yes
OLEContainerControl No! Yes Yes
OptionButton No No Yes
OptionGroup Yes Yes Yes
Page Yes No Yes
PageFrame Yes Yes Yes
Relation No No No
Separator No No Yes
Shape No Yes Yes
Spinner No Yes Yes
TextBox No Yes Yes
Timer No Yes No
ToolBar Yes Yes Yes

The table points out some of the terminology problems in Visual FoxPro. We have a base class named Control. We also refer to the various objects that let users enter data as "controls." And, in fact, something derived from the base class Control might just be a control, but so are a lot of other things. Why couldn't they have picked a different name?

Similarly, there's a base class called Container, but a lot of the other base classes are containers, too. Doesn't English have enough words to go around? Do we have to overload a few of them so badly?

To complicate this particular issue even further, Container and Control are very similar classes. They're both designed to let you create complex controls (the input kind) built out of multiple objects. The difference is that objects based on Control don't allow other objects access to the contained items, while objects based on Container do. In other words, using Control as the basis for an object is kind of like protecting all its member objects.

Not Quite All There

Since in true OOP, every class can be subclassed, we like to think of those base classes that can't be subclassed in the Class Designer as being "half-classed." For the most part, each of these classes is a necessary component of some container class. You can subclass these classes in code, but you still can't incorporate your subclasses in the containers (or subclasses of them) that normally contain the base classes. For example, you can subclass Grid, but your subclass will still be made up of Columns, which will still contain Headers. Similarly, OptionGroups always contain OptionButtons; you can't base an OptionGroup subclass on a subclass of OptionButton.

Even with CommandButtons (which you can subclass), when you make a CommandGroup, it's always built of CommandButtons--you can't build it out of a subclass.

We can see the reason for this limitation, but we hope it will go away in future versions.

The details of each of the base classes are discussed in the Reference section. So we won't go into them here.

Ain't Nobody's Default But My Own?

Certain behaviors are built into Visual FoxPro's base classes. For example, when you press a key, that keystroke is placed in the keyboard buffer. When there are tables or views in the DataEnvironment, you don't need to write code to open them and set up the specified relations. Generally, these behaviors are tied to certain events. The keystroke entering the keyboard buffer is part of the KeyPress event. Opening tables and setting relations is part of the OpenTables method (which behaves more like an event in many ways).

Even if you override the method code for these events with your own code, these default behaviors occur anyway. And that's a good thing. You wouldn't want to have to code your own version of putting a keystroke in the keyboard buffer or opening the tables in the DE. Nor would you want to have to call up to the base class every time you override a method.

But, once in a while, you want to override the default behavior as well. Perhaps you want to eat the keystroke because you're doing something special with it. Sure enough, there's a way to handle it. To prevent an event from firing its default base class behavior, put the keyword NODEFAULT somewhere in the method for that event. Since the base class default behavior always happens last, NODEFAULT can go anywhere in the method code.

Give Me the Whole ScOOP

We've just skimmed the surface on object-orientation here. After working with it for about a year, the OOP way of looking at things is beginning to feel natural to us. So many tasks are performed more simply using OOP.

But using OOP effectively is more than just a code thing. Designing applications to take advantage of OOP requires a new way of looking at them. Check the appendices for some references on object-oriented analysis and design.

BACK TO THE TOP


Combo Box, List Box

These two controls let a user choose one item from a specified, generally scrollable list. In addition, combo boxes can be configured to let the user enter a new item as well (hence the name combo--it's a combination of a text box and a dropdown).

These are incredibly versatile, powerful controls, far more so than the 2.x equivalents, the dropdown and the scrollable list. Some days, we might even argue that combos and lists are more powerful than grids.

Most of the properties of these controls even do what they're supposed to, though there are some glitches along the way. The only problem we've found that doesn't seem to have a work-around is that, due to bugs in a couple of events, there's no hook to get at a combo just before it opens.

    PROPERTY             VALUE                     PURPOSE
----------------    -----------------   ----------------------------------
ColumnCount Numeric The number of columns displayed in a list or in the dropdown portion of a combo.
ColumnLines Logical Determines whether the lines between columns are visible.
ColumnWidths Character A comma-delimited list of the widths for each of the columns. Required with proportional fonts to make straight columns.
DisabledItemBackColor, Numeric The colors used to represent DisabledItemForeColor disabled items in the list or combo.
DisplayValue Character Determines which item in a combo or Numeric is displayed when the combo is closed (and displays first on the list when it opens). For lists, determines where the highlight lands. Always the first column.
FirstElement, Numeric Determine which column of an NumberOfElements array is displayed (one-column list or combo only) and which rows of the array are included.
IncrementalSearch Logical Determines whether the list or combo uses FoxPro's smart incremental search technique or Windows' dumb one.
ItemBackColor, Numeric The colors used for the items in ItemForeColor the list or combo. For a combo, affects only the dropdown portion.
ItemData, Array containing Can contain numeric data matched ItemIdData numeric to the items in a list or combo.
List, ListItem Array containing Contain the actual data from the character list or combo.
ListCount Numeric The number of items in the list or combo.
ListIndex, Numeric The position of the currently ListItemId selected item in the list or combo.
MoverBars Logical List only. Determines whether the list has mover buttons which let the user reorganize the items.
MultiSelect Logical List only. Determines whether multiple items can be selected at the same time.
NewIndex, NewItemId Numeric The position of the most recently added item in the list or combo.
RowSource Character The data in the list or combo or a pointer to it. The actual meaning is determined by RowSourceType.
RowSourceType Numeric The form of the data. Determines the interpretation of RowSource.
Selected, SelectedId Array containing Indicates, for each item, logical whether it's currently highlighted.
SelectedItemBackColor, Numeric The colors used for highlighted SelectedItemForeColor items.
SelLength, SelStart Numeric Combo only. The length and starting position of highlighted text in the text box portion of the combo.
SelText Character Combo only. The highlighted text in the text box portion of the combo.
Sorted Logical Determines whether the items in the list or combo are sorted alphabetically.
Style Numeric Combo only. Specifies either combo box or dropdown list box.
TopIndex, TopItemId Numeric The position of the first item displayed in the list or combo.

EVENT PURPOSE --------- ------------------------------------------------------
DownClick Supposed to fire when user clicks on down arrow on scroll bar. Doesn't fire for lists, only for combos.
DropDown Supposed to fire after user clicks down arrow to open combo, but before combo opens. Actually fires after combo opens.
UpClick Supposed to fire when user clicks on up arrow on scroll bar. Doesn't fire for lists, only for combos.

METHOD PURPOSE -------------------- -----------------------------------------------
AddItem, AddListItem Add a new item to the list or combo.
Clear Remove all items from the list or combo.
IndexToItemId Convert between the two numbering schemes for ItemIdToIndex items.
RemoveItem, Remove an item from the list or combo. RemoveListItem
Requery Refresh the list or combo to include the latest data from the RowSource.


You've probably noticed that many of the properties and methods come in pairs. This is because there are two ways to address each item in a list or combo. The "index" properties address items by their current position in the list. The "itemid" properties address the items by a unique, permanent id number assigned when the item is added to the list. See ListIndex for more on this.

The behavior of lists and combos with numeric data confuses people. (Actually, it was confusing in FoxPro 2.x, too.) The key point is that, even when we think we're seeing numbers in a combo or list, we're actually seeing characters. Combos and lists cannot show numeric data. If you specify a RowSource that's numeric, FoxPro internally converts the data to character before displaying it.

The Value of a list or combo (and, therefore, the ControlSource it's bound to) can be either character or numeric. If the Value is character, it contains the text of the currently highlighted item. If the Value is numeric, it contains the index of that item. Where people get confused is in expecting a numeric Value or ControlSource to contain the item itself if the RowSource is numeric. It didn't work this way in 2.x and it doesn't work this way in Visual FoxPro.

We think it's right as it is, but it does mean you can't feed numeric data directly to the ControlSource of a list or combo. There are a couple of ways to handle this. One is to make ControlSource character, then use VAL() to convert it in the code that uses the control. Typically, you have created a numeric field for a reason (like you need to do math on it) and this isn't such a good idea. The other alternative is to do the conversion (by grabbing THIS.List[THIS.ListIndex]) in the list or combo's InteractiveChange method and put the result where it can be found. We think the best approach is probably to create subclasses of lists and combos for this situation and add a property to hold the numeric value we want to return.

Example

* This code creates a subclass of ListBox
* which is, by default, based on an array called aItems
* which is a property of the list itself.
* The list is multi-select and displays
* a message whenever an item is highlighted.
DEFINE CLASS ArrayList AS ListBox

  DIMENSION aItems[1]

  RowSourceType = 5
  RowSource = "THIS.aItems"
  MultiSelect = .T.

  PROCEDURE InteractiveChange
 
  WAIT WINDOW "Got one!"

  ENDPROC
ENDDEFINE

See Also

AddItem, AddListItem, Clear, ColumnCount, ColumnLines, ColumnWidth, DisabledItemBackColor, DisabledItemForeColor, DisplayValue, DownClick, DropDown, FirstElement, IncrementalSearch, IndexToItemId, ItemBackColor, ItemData, ItemForeColor, ItemIdData, ItemIdToIndex, List, ListCount, ListIndex, ListItem, ListItemId, MoverBars, MultiSelect, NewIndex, NewItemId, NumberOfItems, RemoveItem, RemoveListItem, Requery, RowSource, RowSourceType, Selected, SelectedId, SelectedItemBackColor, SelectedItemForeColor, SelLength, SelStart, SelText, Sorted, Style, TopIndex, TopItemId, UpClick

BACK TO THE TOP


ZOrder

ZOrder is a method of controls that manipulates which control is on top of a stack of overlying controls, but far more important (and far less documented!), it also controls the firing order of key events for a set of controls on a form. No equivalent property for querying who's on top makes this a pain in the neck to work with.

Usage

oObject.ZOrder(nWhichWay)


PARAMETER   VALUE                          MEANING
---------   -----     ------------------------------------------------
nWhichWay     0       Bring the designated control to the top of the
                      stack.
              1       Push the designated control to the bottom of the                       stack.

As is badly explained in the Help file, ZOrder does maintain visual order of controls that might partially overlay each other. It could be incorrectly postulated from the tone of the topic that there might be an equivalent method for the graphical elements drawn on a form with the Line, Box, and Circle methods. Nope. As we explain the reference sections for those elements, graphical elements drawn with these methods are completely passive. While they appear to be "on top" of our controls when drawn, they are, in fact, just the last thing to be drawn on the form, and will skulk to the back if the "underlying" controls are brought forward by gaining the focus, being refreshed, or having their ZOrder messed with.

ZOrder serves a second function, one that has bollixed us good on a few occasions. ZOrder not only controls the appearance of controls within a form, it also controls the firing order of those controls during key events such as Init, Refresh, and Destroy. This can be a distinct and separate order from the TabIndex, which determines the order in which an operator moves from control to control. It is also different from the PageOrder property of PageFrames, which determines the display order of pages within the pageframe. ZOrder'ing a page to the front brings it to the top, but does not alter its order within the PageFrame.

We suppose that of necessity there must be some logic to the firing order of controls. FoxPro must be keeping a list somewhere of all the controls it needs to work with, because it needs to go through that list and run them all. As explained in Section 1, A Gala Event, controls fire Init events from the innermost level out--a text box contained within a column within a grid within a pageframe fires first, followed by the column, grid, and pageframe, in that order. During the Destroy process, the opposite occurs, events firing inward, as if the form implodes. But what of controls on the same level? If three text boxes are placed on a form, which fires first?

Some of the answer can be found within the ZOrder method. Controls on the same level of a container--form, form set, pageframe, whatever--fire in the order they were added to the form, unless they have been moved from that order. In design mode, the "Send to Back" and "Bring to Front" options manipulate this order. This order is saved, not as a explicit property of our controls, but rather is implied in the order in which controls are saved. In a runtime environment, the ZOrder method provides the same ordering function. The frustrating thing is that we do not ever get to directly determine where we are in this list, only push a control to the front or back.

Have some patience with our explanation and we hope all will be clear when we're done. Take a deep breath, let it out slowly. Good. Now read the following paragraphs a few times, fighting to stay calm and keep the hysteria in check. It's really not that bad.

On a visually-designed form, the controls' Init events fire in the order they were added to a form. This is also the default TabIndex order, the order in which the operator will move through the form if he or she presses Tab to go from control to control. Fine. The TabIndex order may be re-arranged by using the Tab Order option on the View menu pad or by directly changing the TabIndex property of each control. This does not change the ZOrder. On the other hand, using the "Send to Back" and "Bring to Front" options on the Format menu changes the ZOrder of the controls. A control sent "to the front" is the last to fire its Init, and the first to be destroyed. An element sent "to the back" acts as if it were the oldest element on the form, firing its Init first, and being the last control to be destroyed.

If the form is generated from code rather than an SCX, each Init fires in the order it is specified with ADD OBJECT in the form's definition, and is unaffected by ZOrder. Our third example demonstrates this. In a coded form, all the Inits still fire, regardless of whether you ZOrder a control to the top of the stack. In a visually-designed form (an SCX) an Init which forces its control to the front [by issuing the command THIS.ZORDER(0)] will prevent the other controls' Inits from running. Bear this in mind if you mess with ZOrder and try to convert a form from visual to hand-coded.

While a form is up and running, a control may be programmatically sent to front by calling the control's ZOrder(0) or to back with the equivalent ZOrder(1). This has a crucial message for us--don't mess with the ZOrder while program execution is within one of these events without a great deal of care. As shown in the example below, it is relatively easy to get stuck in a loop with two controls insisting "Oh, no, after, you," like Chip 'n' Dale. The flip side of this is that a control that teleports itself to the "top" of the stack of controls can short-circuit the stack of other controls waiting to fire their methods, causing a debugging nightmare. Sometimes a Refresh will fire and sometimes it won't? We can see some nice possibilities for this, but if you decide to resort to this tricky method, document it explicitly. Remember--some poor fool, perhaps even you, is going to have to debug this monster long after you've forgotten this trick.

Finally, Destroy events fire in the sequence of ZOrder in effect at the time the Destroy sequence starts. Mess with them within the Destroy event at the risk of your sanity.

During the Destroy events, attempting to push the ZOrder of a control back "over" controls which may have already been destroyed can crash FoxPro with an "Invalid Page Fault."

Example

PRIVATE nDirection

* 1st example: Looping form
* Press any key other than Enter or Spacebar to stop the show.
WAIT WINDOW "A infinitely looping form"
nDirection = 1  && force the control to the back
oForm = CREATEOBJECT('BadForm')
oForm.Show()
oForm.Release()

* Second example: Button2's Refresh never fires
WAIT WINDOW "Form which skips Button2's Refresh"
nDirection = 0  && force the control to the front
oForm = CREATEOBJECT('BadForm')
oForm.Show()
oForm.Release()

* Last example: Inits in coded forms all fire
WAIT WINDOW "Form with Inits which should skip but don't"
nDirection = 0  && force the control to the front during Init
oForm = CREATEOBJECT('BadForm2')
oForm.Show()
oForm.Release()

RETURN

DEFINE CLASS BadForm AS Form
  ADD OBJECT btnCommandOne AS btnCommand WITH Top = 10
  ADD OBJECT btnCommandTwo AS btnCommand WITH Top = 50
ENDDEFINE

DEFINE CLASS btnCommand AS CommandButton
  PROCEDURE Refresh
	WAIT WINDOW "Hello, I'm " + this.name + ;
		"'s Refresh Event" + chr(13) + ;
		"Refresh Events fire in ZOrder()." + chr(13) + ;
		"Press Enter to continue," + ;
		" any other key to quit " ;
		TO cTheirAnswer
	IF EMPTY(cTheirAnswer)
		This.ZOrder(nDirection)  && change order
	ENDIF
  ENDPROC
ENDDEFINE

DEFINE CLASS BadForm2 AS Form
  ADD OBJECT btnCommandOne AS btnCommand2 WITH Top = 10
  ADD OBJECT btnCommandTwo AS btnCommand2 WITH Top = 50
ENDDEFINE

DEFINE CLASS btnCommand2 AS CommandButton
  PROCEDURE Init
	WAIT WINDOW "Hello, I'm " + this.name + ;
		"'s Init Event" + chr(13) + ;
		"Hand-coded Init Events all fire " + ;
		"regardless of ZOrder()." + chr(13) + ;
		"Press Enter to continue"
	This.ZOrder(nDirection)  && change order
  ENDPROC
ENDDEFINE

See Also

Box, Circle, Line, PageOrder, TabIndex

BACK TO THE TOP