Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
Toolbars are unique in Visual FoxPro. They’re neither controls nor forms, yet they have some of the behaviors of each. But they also have some behaviors all their own. A toolbar is truly a bar (not a form) full of tools. Separators are special objects that can exist only in toolbars—their role in life is to put some space between different groups of controls on the toolbar.
From the user’s point of view, think of toolbars as mouse shortcuts. Instead of opening a menu popup and clicking on the desired item, the user can choose from the toolbar. Good interface design says that anything the user can do with the mouse, she can do with the keyboard, too, so don’t view toolbar items as replacements for menu items—only as other ways to access them. Sadly, the Visual FoxPro interface itself breaks this rule all the time, and toolbars are one of the worst culprits. The Form Controls toolbar, for example, has no menu counterparts—if you can’t use a mouse, you can’t design forms in Visual FoxPro. That’s inexcusable.
Toolbars are containers—they hold controls. You can put just about any control in a toolbar, but we recommend you limit yourself to command buttons, option buttons, check boxes, combo boxes, timers and maybe some ActiveX controls. (For option buttons and check boxes, you’ll probably want the graphical kind.) The others are just too unwieldy for toolbars.
Like forms, toolbars can exist on their own. You can create a toolbar, then Show it and it’s a live object. Toolbars can be added to form sets, but not forms. Adding a toolbar to a form set is weird, though. Once you’ve turned the individual form into a form set (by choosing CREATE FORM
Set from the Form menu), you add a toolbar by choosing it from the Form Controls toolbar as if it were a control. But the toolbar doesn’t go onto another form—it becomes an independent member of the form set.
Toolbars can be “docked” at any side of the screen. Docking a toolbar means attaching it so it becomes part of the border. When you dock a toolbar, it reshapes itself to be as narrow as possible against the screen edge. That is, when you dock it at top or bottom, it gets as short as possible. When you dock it at either side, it gets as narrow as possible. Docked toolbars have no title bar. The user can dock toolbars by dragging the title bar to a screen edge, or by double-clicking on the title bar or any space not occupied by a control. Similarly, double-clicking on empty space or dragging that space off the edge undocks a toolbar.
A toolbar’s size is determined by what it contains. When a toolbar is undocked, you can reshape it. Dragging on a side or corner doesn’t enlarge or shrink the toolbar—it changes its shape. Changes to its Height and Width might reshape it, but the actual values you get for Height and Width are determined by the contents.
A toolbar can get focus only if it contains a control that allows text input (text box, edit box, spinner, or combo box). Clicking into one of those objects moves focus to the toolbar. Clicking on any other control on a toolbar leaves focus where it was before (unless the clicked control changes the focus). Even when a control on a toolbar has focus, the toolbar itself is never _SCREEN.ActiveForm. In addition, the Valid and LostFocus events of the form control that previously had focus do not fire, though the GotFocus of the toolbar control does. You cannot tab between controls on a toolbar even if they can get focus.
Property |
Value |
Purpose |
ActiveControl |
Object |
References the control on the toolbar that has focus. Normally, this property contains a value only briefly while the control responds to being clicked. A few controls can actually hold focus and stay ActiveControl for more than an instant. |
ControlBox |
Logical |
Determines whether the toolbar (when undocked) has a Close button in the upper right corner. Make sure that users can recall toolbars if they close them inadvertently. Since we think users should generally have control of their environment, we suggest you set this to .F. only in unusual circumstances. |
ControlCount |
Numeric |
The number of controls on the toolbar. |
Controls |
Collection |
References to the controls on the toolbar. |
DataSession |
Numeric |
Determines whether the toolbar has its own private data session. |
DataSessionId |
Numeric |
The ID number of the toolbar's data session. |
Docked |
Logical |
Indicates whether the toolbar is currently docked. |
DockPosition |
Numeric |
Determines whether the toolbar is currently docked and, if so, at which side. |
KeyPreview |
Logical |
Supposedly determines whether controls see keypresses right away or the toolbar gets them first. But there's no corresponding KeyPress event for toolbars, so setting this is useless. |
Objects |
Collection |
A COM collection containing references to all the controls on the toolbar. Also has a Count property. |
ShowTips |
Logical |
Determines whether tooltips for the controls on the toolbar are displayed. |
Event |
Purpose |
AfterDock |
Fires after the toolbar has been docked. |
BeforeDock |
Fires when the toolbar is asked to dock or undock. |
Undock |
Fires after the toolbar has been undocked, but before it redisplays. |
Method |
Purpose |
Dock |
Docks or undocks the toolbar at a specified location and position. |
The ActiveControl property of a toolbar should contain an object reference only briefly, while a control is in use. But if the toolbar contains a control that can get focus (like a text box), it's possible for ActiveControl to get confused. If the text box (or whatever) has focus and you click on another control on the toolbar, ActiveControl becomes more persistent and doesn't always point to the right control. We also ran into one situation where a combo in a toolbar wouldn't give up focus to any other control on the toolbar. |
In VFP 3 and VFP 5, timers in toolbars never fire. (Actually, we've heard they fire under some very specific, very weird circumstances, but that's maybe even worse than never.) In those versions, don't put a timer in a toolbar. However, you can create a timer and store a reference in a property of the toolbar. Tamar wrote an article about toolbars that enable and disable their controls using a timer this way. The good news is that this bug is fixed, so timers in toolbars do fire in VFP 6 and later. |
If toolbars are hybrid objects, separators are just barely objects at all. You stick them into toolbars to visually separate consecutive controls. They let you group similar functions together and keep them apart from other groups. All in all, pretty useful.
But separators are pretty basic. In VFP 7, they’re up to a grand total of 21 PEMs, the fewest of any of FoxPro’s base classes. Even though they have a position on a toolbar, separators don’t have Left or Top properties—their position is determined by the position of the objects around them. Before VFP 7, you can’t determine anything about their appearance (or is that lack of appearance?). Starting in that version, you can do one thing to separators. They acquired the Style property, which determines whether the separator is invisible (the default) or a single line. This ability lets you visually divide a toolbar into discrete sections. Unfortunately, even with Style set to a line, separators are still invisible at design-time. Among other things, this can make it difficult to get them where you want them.
Help says that the line you get with Style = 1 is vertical, and the Property Sheet calls it a "Vertical Rule". Well, it is ... some of the time. But when a toolbar is docked to the side, the separator's line switches to horizontal, as it should. It's only Help that's wrong. |
VFP's toolbars aren't as rich as those offered in some other products (like Word). In those products, when an undocked toolbar is reshaped, the separator line rule becomes horizontal, when appropriate. In VFP, a horizontal rule is visible only when the toolbar is docked. We debated about whether to call this a bug or "by design" and decided that we really think the Word behavior is correct. |
Separators are actually a lot more capable in VFP 6 and later than in previous versions. Starting in that version, they have their own Init, Error and Destroy events. (Actually, we suspect all VFP classes are now derived from a single base class that includes these events.) Though we’ve never subclassed separators except for testing purposes, the new additions make it more likely that we’ll do so, in the future, just as soon as we figure out what we’d want a separator subclass to do. Some of our friends who do heavy object systems (with thousands of objects interacting) have used separators as a starting point, because they have so little “baggage” in terms of built-in functionality. (Relation is another object that has a small footprint.) We haven’t benchmarked it, but we think these lightweight controls might be good for that purpose.
* Set up a toolbar class that keeps the user informed
* as it docks and undocks.
DEFINE CLASS AnnoyingToolBar AS ToolBar
PROCEDURE BeforeDock
LPARAMETERS nLocation
LOCAL cLocation
DO CASE
CASE nLocation = 0
cLocation = "Top"
CASE nLocation = 1
cLocation = "Left"
CASE nLocation = 2
cLocation = "Right"
CASE nLocation = 3
cLocation = "Bottom"
ENDCASE
IF nLocation = -1
WAIT WINDOW "Hey, I'm undocked!"
ELSE
WAIT WINDOW "Getting ready to dock at "+cLocation
ENDIF
ENDPROC
PROCEDURE AfterDock
WAIT WINDOW "Finished docking"
ENDPROC
PROCEDURE UnDock
WAIT WINDOW "Undocked"
ENDPROC
ENDDEFINE
Other than being annoying, the class in the example is good for showing the sequence of toolbar events. When you dock the toolbar, BeforeDock fires before AfterDock (well, that makes sense). But when you undock a toolbar, Undock fires before BeforeDock. Got that?
The firing sequence was different in VFP 3. See BeforeDock for the details.
ActiveControl, AfterDock, BeforeDock, ControlBox, ControlCount, Controls, Dock, Docked, DockPosition, FormCount, Forms, Objects, SpecialEffect, Style, Undock