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.
Option buttons come in bunches, where only one can be chosen at a time. Option buttons are good for sets of mutually exclusive options. (For example, groups of ages such as 0–12, 13–24, 25–49, 50–64, and 65 and above.)
Since options come only in sets, we can’t create them individually. An OptionGroup is a container that holds option buttons. It’s the group that has a Value, not the individual buttons. (Though, for some strange reason, individual option buttons do have ControlSource and Value—don’t ask us why. We think they forgot to remove them when they added OptionGroups.)
Like check boxes, option buttons can be textual or graphical. With graphical option buttons, the currently selected button stays depressed. If the group’s Value doesn’t correspond to any of the buttons (which shouldn’t really occur, but FoxPro lets you do this), none of the buttons is down.
Property |
Value |
Purpose |
AutoSize |
Logical |
Determines whether the group grows and shrinks to match the sizes of the buttons within. |
ButtonCount |
Numeric |
The number of buttons in the group. |
Buttons |
Collection |
References to the individual buttons in the group. |
Objects |
Collection |
References to the individual buttons in the group. |
Value |
Numeric |
The number of the currently chosen button in the group. |
Character |
The caption of the currently chosen button in the group. |
Property |
Value |
Purpose |
Alignment |
Numeric |
Determines which side of the caption the button appears on. |
AutoSize |
Logical |
Determines whether this button resizes itself when the caption changes. |
Caption |
Character |
The text for the button. |
Picture, DownPicture, DisabledPicture |
Character |
The names of graphics files used for a graphical option button. Picture is used when the button is not chosen. DownPicture is used when the button is chosen. DisabledPicture is used when the button is disabled. If either DownPicture or DisabledPicture is not specified, Picture is used in that circumstance. |
SpecialEffect |
Numeric |
Determines the physical appearance of the button: 3-D (0); Plain (1), meaning flat; or Hot Tracking (2), meaning flat until it gets focus or the mouse is over it. Hot Tracking is applied only to graphical option buttons (Style = 1); ordinary option buttons with SpecialEffect set to 2 are flat. |
Style |
Numeric |
Determines whether the button is textual or graphical. |
TabIndex |
Numeric |
The "tab" order of the button within the group. |
Unlike FoxPro 2.x radio buttons, option buttons can be organized any way you want within the group. You can line them up vertically or horizontally, put them in rows and columns, or arrange them totally randomly. Whatever arrangement you choose, we recommend you set the button’s TabIndex to match it in some way.
Despite its name, TabIndex doesn’t have much to do with the Tab key. With KEYCOMP set to Windows, you can’t actually tab between buttons in a group. You have to use the arrow keys. TabIndex controls the order you see when you do that. (With KEYCOMP set to DOS, Tab does go through all the buttons in a group before moving on to the next control, but who sets KEYCOMP to DOS in a VFP app?)
Both individual buttons and the group as a whole have AutoSize properties. In some cases, the interaction of these is a little weird. If you set AutoSize on for the individual buttons, but not for the group as a whole, you may lose part of some captions. Same thing if you turn on AutoSize for the group, but not for the individual buttons. We think you’ll usually want AutoSize on for both. The group’s AutoSize is especially useful when you want the buttons arranged other than in the default column.
Starting in VFP 7, both option buttons and option groups have MouseEnter and MouseLeave events. The events at the two levels are mutually exclusive. That is, the group’s MouseEnter fires when you first enter the group. Then, when you move the mouse over a button, the group’s MouseLeave fires, then the button’s MouseEnter. When the mouse leaves the button, the button’s MouseLeave fires, followed by the group’s MouseEnter. The sequence is the same even if an individual button is flush with (or even beyond) the edge of the group.
At first glance, it looks like subclassing option buttons and groups is a little easier than with other container-contained pairs. You can subclass both OptionButtons and OptionGroups in the Class Designer. But that’s as far as it goes. You still can’t tell your OptionGroup subclass to use your OptionButton subclass instead of the default. You can do all this in code, but it requires a lot of messing around.
When you do go ahead and subclass OptionGroup with the default OptionButtons, you run into a counting problem. You’re stuck with however many buttons you put in the group. When you put your subclass in a form, you can’t reduce the number of buttons and, if you increase ButtonCount, the new buttons don’t have whatever special settings drove you to subclass the thing in the first place. If you create your subclass with no buttons, then all the buttons you add have to be set up appropriately. Your best bets are probably to create a group with no buttons and use AddObject to add your button subclass at runtime, or to use the default buttons, but do most of your work in the OptionGroup subclass and make liberal use of SetAll.
OptionGroups and OptionButtons share a behavior with CommandGroups and CommandButtons that has to do with the nearly symbiotic relationship between the buttons and the group. Unlike other container/contained pairs, when an option button doesn’t have code for a particular method, the group’s code for that method fires. As far as we’re concerned, this is just one more reason to write all your code in the OptionGroup and forget that OptionButtons have methods.
MouseEnter and MouseLeave don't follow this paradigm. Even when a button's MouseEnter or MouseLeave is empty, the group-level events don't fire as the mouse passes into or out of the button. But, as noted above, the reverse events do. That is, the group's MouseLeave fires as you enter the button, and the group's MouseEnter fires as you leave the button. |
As with page frames, we’re hoping for some great builders in this area.
* Here's one possible approach to subclassing
* (except we'd really do this in the Class Designer, not code).
DEFINE CLASS MyOptionGroup AS OptionGroup
ButtonCount = 0
AutoSize = .T.
PROCEDURE Init
* Set up the buttons here. You won't see the
* changes at design-time, but they'll be
* right at runtime.
This.Setall("Alignment", 1)
This.Setall("AutoSize", .T.)
ENDPROC
ENDDEFINE
Alignment, AutoSize, ButtonCount, Buttons, Caption, DisabledPicture, DownPicture, MouseEnter, MouseLeave, Objects, Picture, Set KeyComp, SetAll, SpecialEffect, Style, TabIndex, Value