Controls and KAOS
Who controls the past controls the
future;
who controls the present controls the past.
George Orwell, 1984
Controls are the means of interacting with users. Some, like the text box and the command button, are visual elements of the user interface; others, like the timer and the session controls, don't have a visual element, but work behind the scenes to support the user interface. Understanding the intricate ways in which the properties, events and methods are evaluated and executed is essential to grasping how Visual FoxPro works. In this section, we'll discuss what controls are and some of the features common to controls. We'll go on to look briefly at each control (you'll find more in the Reference section), and discuss where it's appropriately used. At the end of the discussion, we'll explain how the built-in characteristics of controls can be extended to create more complex and customized controls, tailored to meet your clients' needs, capable of interacting with your users in ways beyond those anticipated by Visual FoxPro's designers.What's a control?
Most controls are objects that are manipulated, like light switches, sliders or radio buttons, and hence can be thought of as computer representations of real-world controls. This metaphor falls a bit short, however, for some controls, such as the timer, which doesn't have a visual representation, is invisible to the end user, and can't be "grasped." For our purposes, we think a sufficient definition is that controls are the objects on forms that perform specific actions when acted upon by an event generated by the user interface, program code or the system.Controls can be placed on a form in a number of ways. The simplest is to use the Form Controls toolbar. However, some of the other approaches offer more power, including dragging fields from the Data environment or controls from the Component Gallery. Properties, events and methods contain the information that determines what a control looks like, what characteristics it has, and what it should do when acted upon. Hence, the control's appearance and behavior is encapsulated within the control—one of the key principles of object-oriented programming. (See "OOP is Not an Accident" for more on the principles of object orientation and how they're implemented in Visual FoxPro.) The properties of forms and controls are changed through the Property Sheet. Their events and methods can be customized using the method code editor.While there appear to be a dazzling number of properties, events and methods associated with the collection of controls (in fact, there are more than 2,660 combinations of controls with properties, events and methods), many of the properties, events and methods are common to most controls and perform the same function (or a very similar function) for most of them. Hence, they can be understood just as easily with a blanket statement rather than repeating something like "The Top property determines how far from the top of its container the control appears" in each control's reference section. In fact, we don't repeat that information in the Reference section of this book. The tables there list only those PEMs (that's common shorthand for "properties, events and methods") that are of particular interest for that control. The VFP Help does contain complete lists for each control.Here is a quick synopsis of the most common PEMs.Common Control Properties
A property is nothing more than a characteristic of an object. Properties describe things like the object's height, width, color, and whether the object should have certain capabilities, such as being enabled or being visible. Properties are manipulated in Visual FoxPro in pretty much the same manner as memory variables, and can be thought of in many respects as private memory variables scoped to a single control.There are two common ways to manipulate the properties of a control: at runtime using assignment commands and during development using the design mode tools. (In fact, you can manipulate properties programmatically at design-time, too. See "Builders and Wizards and Bears, Oh My!" for more on this.) To refer to properties programmatically, use the "dot notation" explained in "OOP is Not an Accident"—as in:
ThisForm.PageFrame1.Page7.Command1.Caption = "OK"You're probably thinking, "I've got to type all that?" No, not always; rarely, in fact. IntelliSense is there to help out. See "IntelliSense and Sensibility" for more details on how IntelliSense helps make these object references easy to get into your code.Properties are assigned visually using the Property Sheet. Formally, Microsoft calls this the "Properties Window" but we've gotten used to the "Sheet" terminology, especially since this window is often set to be "always on top" (see the right-click menu for that option)—something that seems more intuitive for a sheet than a window. We've heard it variously called the "Properties Form," "Property List," "Properties Sheet," "Property Sheet" and "PropSheet." We think all are fine, as long as folks know what you're talking about. Let's review some of the more common properties and what they are usually used for:The Name property of a control does what it says: tells you the control's name. You use the name to refer to the control programmatically.Parent provides an object reference to the object containing the control. For a control sitting right on a form, for example, Parent holds a reference to the form. For a control on a page of a page frame, Parent contains a reference to the page. Parent helps you climb up from the control you're on to the outside world. This is the containership hierarchy, not to be confused with the inheritance hierarchy—see ParentClass, below, for that.Value holds whatever the control currently contains, generally some variation on the user's input. ControlSource lets you bind the control's Value to a field, variable or property—when Value changes, so does the item named in ControlSource.The control's location and size are specified with the Top, Height, Left and Width properties. All of these measurements are expressed in units determined by the object's ScaleMode—either pixels or foxels. (Stick with pixels—we haven't come across any good reasons to use foxels.)Font characteristics are specified with the FontName, FontSize, FontBold, FontUnderline, FontStrikethru and FontItalic properties—almost all controls have these. The FontCondense, FontExtend, FontOutline and FontShadow properties are oddballs, included for compatibility with the Macintosh but having no effect on Windows fonts. (By the way, if you want to use the Transparent characteristic of fonts, it's only available using the old FoxBase STYLE "T" clause on those commands that support it.)The Comment and Tag properties have no designed use. They're there to provide a place to leave notes or do whatever you want. We haven't seen very many people actually use these properties. On the whole, it seems better to add custom properties as needed. The term Tag, by the way, is a carryover from Visual Basic, and is not at all related to the tags Visual FoxPro uses in its indexes.The Enabled property determines whether an object is capable of reacting to external events like clicking, typing, or tabbing to a control. A disabled control is "grayed-out" and can't be accessed. Color settings for a control can be specified in a number of ways. The most obvious are the properties BorderColor, ForeColor and BackColor, with their equivalents of DisabledForeColor and DisabledBackColor, which apply when the object's Enabled property is False. However, in most cases you'll want to use the ColorSource property instead, so the control's colors are based on the user's Windows system choices. A number of controls also have other color settings that affect particular aspects of their appearance (such as ItemForeColor and FillColor).The BaseClass, Class, ParentClass and ClassLibrary properties describe the pedigree and history of a control, and are fixed at the time a control is created. These allow you, at runtime or design-time, to examine the lineage of a control and act appropriately. A couple of properties help you hook your controls into your Help system. HelpContextID and WhatsThisHelpID each contain an identifier for the control that can link to a Help entry.Every VFP object has the Application property. It contains an object reference to the instance of Visual FoxPro in which that object was created. For self-contained applications (the kind that use only VFP), there's not too much use for this property. However, when a VFP application is started by another application, this reference gives you a handle to the VFP engine. (See "It was Automation, You Know" for an explanation of why another application might run a VFP application.)
Common Control Events
Events introduce some difficulties in talking about Visual FoxPro. When we speak of "adding code to the MouseDown event," it is easy for our audience to sense that an event is somehow a different kind of thing from a method. This is a linguistic issue, not a complication of Visual FoxPro. An event is an occurrence that is communicated to your application. This event might be the fact that the mouse is passing over a control, or that a timer's Interval has elapsed. When your application, via the underlying FoxPro engine, receives this message, a method with the same name as the event is run automatically. So when we talk about modifying the MouseDown event, we are really speaking of "the method associated with the MouseDown event." Events happen. There is nothing we can do about them and certainly nothing modifiable about them. We're not going to keep trying to say "The method associated with the MouseDown event," but since we do want to distinguish an automatically run event from a method, like most VFP developers, we shorten it to "the MouseDown event." |
form1.commandgroup1.MouseEnter(0, 0, 162, 98) form1.commandgroup1.MouseMove(0, 0, 163, 100) form1.commandgroup1.MouseLeave(0, 0, 163, 101) form1.commandgroup1.command1.MouseEnter(0, 0, 163, 101) form1.commandgroup1.command1.MouseMove(0, 0, 163, 101) form1.commandgroup1.command1.When() form1.commandgroup1.When() form1.check1.LostFocus() form1.commandgroup1.command1.GotFocus() form1.commandgroup1.Message() form1.commandgroup1.command1.MouseDown(1, 0, 165, 108) form1.commandgroup1.MouseDown(1, 0, 165, 108) form1.Paint() form1.commandgroup1.command1.MouseUp(1, 0, 165, 108) form1.commandgroup1.MouseUp(1, 0, 165, 108) form1.commandgroup1.InteractiveChange() form1.commandgroup1.command1.Click() form1.commandgroup1.command1.Valid() form1.commandgroup1.Valid() form1.commandgroup1.command1.When() form1.commandgroup1.When() form1.commandgroup1.Message() form1.commandgroup1.Click()Other common events include Resize, which fires when a container's size is changed, and GotFocus and LostFocus, which fire when a control receives and loses focus. Beginning with VFP 6, controls have a bunch of events whose names begin with OLE. These events handle actions related to OLE drag and drop. See "OLE Drag and Drop" in the Reference section for an exhaustive look at this feature.VFP 6 also added a new wrinkle to events with Access and Assign. These events can be attached to any property and fire when the property is either read or changed. Because the existence of these events is under developer control, they add a new dimension to controls. (See the Reference section for details.)
Common Control Methods
Methods are similar to events, in that they contain a block of code to be executed. The main difference is that events are triggered by something outside the direct control of the developer, but methods are called explicitly. Compared to events and properties, there are not many common methods. That's because methods are really the determining factor in creating a unique control—unique behaviors of an object indicate that it needs to be considered a unique control. This is one of the reasons that Microsoft decided to combine the FoxPro 2.x "Invisible Button" and "Push Button" controls into a single CommandButton in VFP; they are functionally identical, differing only in a few properties. However, ComboBox and ListBox are two separate controls. Although they share a number of methods, there are enough differences between them to justify separate structures.The ZOrder method sends the specified control to the front or back of its group of overlapping controls. ZOrder moves items along the Z-axis, the third dimension we simulate on our monitors. But ZOrder is not just for appearance; it also affects the firing order of some events—see the ZOrder entry in the Reference section for more details on this. Some controls contain their own ordering properties (such as Pageframe's PageOrder)—those properties determine the horizontal (X-axis) or vertical (Y-axis) order of the contents. ZOrder determines the depth (Z-axis) order.Move allows movement of controls on a form under programmatic control. Drag and OLEDrag initiate native drag and drop and OLE drag and drop, respectively. The Refresh method redisplays a control, also firing the Refresh method of any contained controls. SetFocus moves the focus programmatically to the control (similar to the function performed by the _CUROBJ memory variable in FoxPro 2.x). AddObject, NewObject and RemoveObject allow the addition or subtraction of controls from container objects. CloneObject creates a duplicate of the control inside the same container.The SaveAsClass method is pretty cool. It allows us to save the definition of any control to a class library, both at design-time and at runtime! This can be really useful in an interactive development session, where you can programmatically alter a live object's properties until you get them just right, and then save your result right from the Command Window, putting the live form into a visual class library.The SetAll method programmatically sets a property of all objects or objects of a particular class within a container. ResetToDefault, added in VFP 3.0b, lets you return a property to its default value—very handy, since a control is created faster when properties are set to default than when they're explicitly assigned the default value. AddProperty, added in VFP 6, lets you add properties to an object at runtime. ReadExpression, ReadMethod, WriteExpression, and WriteMethod let you examine and change properties and methods programmatically. Except for WriteMethod, they're available at runtime, as well as design-time. ShowWhatsThis displays the WhatsThisHelp for the control.The remainder of methods built into Visual FoxPro are typically contained in only a few objects, and are more appropriately discussed in the reference section for that object.Visual FoxPro Controls
Controls can be broken down into groups in a number of ways. We have chosen to focus on them from the end user's point of view—what the end user is going to use each control for. This isn't an exhaustive view of them; it would take another book at least as long as this one to explore all the capabilities of each. We're trying to focus more on the use of each control and to provide some pointers. Further information about specific controls can be found in the Reference section.Text-based controls: TextBox, EditBox, Label and Spinner
These controls allow you to enter one item of text. A text box is typically restricted to a few words or phrases, occupying a single line of form real estate. An edit box, typically bound to a memo field, allows free-form entry of a variable-length block of text from one word to a large narrative, providing scrollbars as necessary. A label simply displays text, and does not allow a user to change it. Spinners are used for numeric entries and allow both direct text entry and selection by means of up or down arrows.Choices: CheckBox, ComboBox, ListBox and OptionGroup
These controls allow the user to select the most appropriate answer or answers from a set of choices. Check boxes are used for on/off, true/false-type answers. Option groups also allow only one answer, though typically from a larger number of choices. Combo boxes and list boxes allow the choice of one or more answers from a list. Normally, check boxes and option groups are defined during the design of a form to have a particular shape and prompts. The two kinds of lists, on the other hand, tend to be more flexible, allowing population at runtime. The nice thing about the Visual FoxPro object model is that the properties of these objects are available at runtime, so the rules above are typical guidelines and not carved in stone.Check boxes, in the Windows interface, normally are used just as an on/off, true/false switch. Occasionally, though, a check box is used as a button that immediately fires further actions, such as calling up a dialog. (See the Project Class check box on the Projects page of VFP's Options dialog, for example.) Our take is that this use of a check box, even when the prompt ends in an ellipsis, is bad design, since it confuses users. Use buttons when you need them and save check boxes for indicating if something is or it isn't.Option groups also allow only one choice, and are typically sized and populated with their choices at design-time. You can arrange the choices in any visual configuration—you're not limited to rows, columns or even grids of them.Combo boxes, formerly known as drop-downs or popups, take up little real estate when inactive. List boxes take a larger piece of screen space, but allow the user to see several options at once. Both combo boxes and list boxes can be populated from a number of sources, ranging from a hard-coded list entered at design-time to a list programmatically generated at runtime from an array, table or query. Both can handle multi-column lists. List boxes have the added advantage of allowing for multiple selection, and for the addition of mover bars to let the user move things around in the list. Combo boxes, on the other hand, can be configured to allow the user to enter data not on the list. Whichever you use, keep in mind that these controls are meant for choosing from small- to medium-sized lists and not for choosing among thousands or tens of thousands of options. Your users will not be amused as they scroll through the choices one at a time. (Despite the incremental search available in both controls, some users will scroll through one at a time.) In general, list boxes are suited to somewhat larger sets of choices than combo boxes.Using combos and lists with a huge number of items has been a perennial source of bugs and performance issues in FoxPro. VFP 3 had problems with multi-selection of more than about 60 items on a list; FoxPro 2.x actually crashes under some conditions with 600 or more items in the list. Not only is this poor interface design for us, but it is also difficult for the Fox team to program in the Windows environment. Avoid very large data sets for these controls if at all possible.Actions: CommandButton, Timer and Hyperlink
The action controls are used to fire an action immediately upon selection. Command buttons are variously referred to as "push buttons" or just "buttons." (For whatever reason, option buttons are rarely, if ever, called just "buttons.") Command buttons can show text or a bitmap, or be invisible to create a "hot" region on the form. (Don't confuse the last option with the Visible property—an invisible button is still an active participant in the form, but any control with its Visible property set to False is both invisible and disabled.) Timer controls might seem like a bit of an odd duck in this category, but timers are unique no matter how you categorize things. They are the only invisible controls that directly cause actions to occur, unlike the more passive Custom class objects. But they do cause actions, which is why we plunked them here. Timers are a welcome addition to our arsenal, allowing us to dynamically take a look at the current state of a form to check status, update displays, and so forth.Hyperlinks, added in VFP 6, are also unusual. Like timers, they're invisible, but unlike timers, they don't have any special events that fire on their own. What they do have, though, is a couple of methods useful for manipulating a browser. The most important is NavigateTo, which can open a browser looking at a particular Web page or file.Containers: Grid, Column, PageFrame, Page, CommandGroup, Container, Control and Toolbar
Containers are objects that can contain other controls. A container class is a great place to put a chunk of code that should affect all of the contained objects, like the logic to turn off navigation buttons when at the top or bottom of the data set, or to disable visually contained controls when their container is disabled.Grids contain columns, which in turn contain other controls. Some of the contained controls are specific to grids, such as Columns and Headers, while the individual cells hold the controls suited to the field, like text boxes, check boxes, combo boxes and the rest. Grids are normally used to display the contents of a table, like the Browse of old. Unlike Browse, grids are incredibly configurable, with the ability to use different controls for different data, change colors and fonts on the fly, and much more. They're also incredibly difficult to get just right for data entry—many of the developers we know would rather climb Mount Everest than configure a grid for entering data. As a display device, though, or a tool to select a particular record, they're hard to beat. A fellow developer was heard to say, after evaluating a number of other ActiveX data grids, that the only thing worse than the VFP Grid was any other choice. Page frames allow the creation of tabbed or tabless sets of pages. The hottest thing around when VFP 3 first came on the scene, tabbed dialogs have already fallen out of favor to some extent, but they still have their uses. A page frame contains pages, which contain controls.Command groups contain sets of command buttons. These seemed terribly important when VFP was new, but much less so now. Using individual buttons provides more control and greater flexibility. Bundling command buttons with other controls like check boxes and combos in a container provides nearly all the benefits of command groups, but with added flexibility.The other three classes in this category, Container, Control and Toolbar, are not exactly controls in their own right, but they are container objects. Controls (isn't this confusing?) and toolbars cannot be directly created from the Form Controls toolbar, unlike the other controls listed in this section, including Containers.
Yet another mangled piece of overloaded terminology here. The Container class is one of a number of classes, all of which are referred to as "container classes." To confuse matters more, Control is a class, but "controls" refers to a whole set of classes, and "control" is a general-purpose term for any member of that set. We try to distinguish between the individual Container class and the set of container classes, and between the Control class and the controls, with our use of capitalization, but, as in the heading above, we don't always succeed. It looks like we're stuck with these names for the long term, so bear with us. |
Graphical Elements: Image, Line, Shape and Separator
These graphical items can be used to segregate related areas, draw on the form, display images, or separate related groups of buttons on a toolbar. Unlike the graphical elements in FoxPro 2.x and the Box and Line methods of forms, these are full participants in the form, and can react to clicks and drag-and-drop events. (We are amused that Separators are on the Form Controls toolbar when you can't put them on a form. But the same toolbar is used in the Class Designer; in addition, a toolbar can be added to a formset in the Form Designer, so it does make sense.)ActiveX: OLEBoundControl and OLEControl
VFP's ActiveX container controls have some very special functionality: They allow Visual FoxPro to open a window into another application, and to control the display and functionality of that application. OLEControls also are used to add third-party ActiveX controls to forms, giving the form capabilities beyond those (or better designed than those) built into VFP.It's worth noting that Microsoft created a bit of a mess for itself by including the term "OLE" in the names of these classes. Since they did that, "OLE" has been transformed into "ActiveX"—you'll see a reflection of this in the ToolTip and status bar message if you pass the mouse over these items on the Form Controls toolbar.User Interface Issues with Controls
Much has been written about the importance of good user interface design, and we don't want to beat the issue to death here. (Well, since we're all pretty passionate on this subject, actually we do want to beat it to death, but we won't.) Check out the appendices for some suggested reading on this topic if you haven't dealt with this before. A clean, easily understood and consistent user interface can enhance users' confidence in their abilities with your system, and improve their opinion that they are dealing with a polished and professional application.The most important user interface principle to bear in mind with controls is consistency throughout an application. If a command button with a printer icon calls up a print dialog on one form, a check box with the same icon in another form should not toggle an option that determines whether the output of an action should be printed. The user is bound to call you, swearing the application isn't working the way it did yesterday. All forms should share similar prompts—when completing a form, the users should not have to vacillate between selecting "Close," "Cancel," "OK" or "Quit" button prompts, if the resulting action on the different forms is the same.Think about the way your application is to be used. If the predominant use of a form is for high-volume, heads-down data entry, requiring the user to switch from keyboard to mouse to keyboard is murder on efficiency (and the wrists). On the other hand, a form more likely to be used by someone trolling for new relationships between the pieces of information should be tailored to the more creative "What happens if I click here and select this" operation of a click-happy mouse user. Another consideration is accessibility. All controls in your systems should be accessible by several means. The minimum is to provide keyboard and mouse access. In many cases, you'll also want a shortcut key and perhaps a menu option. This is a convenience for power users, who will discover these shortcuts and use them to run your application faster. But this is more than an issue of sophistication. Many of your users will have a preference for the keyboard over the mouse, or vice versa, and the application should be accessible to them. In addition, users with limited visual or motor skills should be able to operate your application. As more companies become sensitive to this issue of making provisions for their disabled workforce, this issue will become more prominent. Microsoft itself has jumped onto this bandwagon in the last few years and now offers accessibility guidelines for applications, and new accessibility features (including an Accessibility Browser) within Visual FoxPro. See SYS(2800) in the Reference section. The bottom line is that you should plan and design your application from the beginning to accommodate as many interface styles as reasonably practical.Finally, let the user know what is going on. Nothing raises the stress level of a user more quickly than a system that wanders off and appears to hang when they tell it do go do something. If your application will take a while to complete a step, change the mouse pointer to an hourglass, display a progress thermometer or tell the user that the operation will take some time to complete. Give the user a chance to cancel long operations if she doesn't have the time or resources to complete a step. Let the user be in charge.User interface design is not a subject that comes naturally to many of us. Study some of your favorite applications to see how they handle your actions. Maybe more important, study some applications you hate, to see what makes them so miserable to use—and be sure not to do those things in your applications. Read some references in the field. Above all, make the user feel comfortable, confident and in control.Extending the Reach of Controls
The most profound aspect of the many facets of Visual FoxPro is that the developer is not limited to the functionality built into the language, but rather that this functionality is the basis from which the developer can extend the capabilities of Visual FoxPro to the needs of the client. This is true in many different ways, including controls.Custom controls are useful in a number of circumstances. Because controls in an object-oriented hierarchy inherit behavior and characteristics from their parents, a class hierarchy can make it far easier to distribute changes throughout a system. For example, changing all the command buttons in a system to use FixedSys rather than Arial is just a matter of changing the FontName property in the parent class definition upon which all of your buttons are built. In addition, encapsulating small bits of thoroughly tested code at various stages of the class tree ensures a more robust and bug-free final result. Once you have written solid code for a Next button, preserve that button in a class library and use that class wherever the button is needed. If you find a bug in the behavior, you can fix it in one place. If you need additional functionality in some places, you can subclass and refine the class definition.Creating a custom control is easy. Build the control you want, using a blank form in the Form Designer. (If you spend a lot of your time developing custom tools, you may find that a custom "workbench" form can speed the process.) Click the control to select it, and then select the "Save As Class" option from the File menu. Select a name and location for the Visual Class Library in which you'd like to store your custom controls, and then save it. If you prefer, you can just start with the Class Designer—it looks a lot like the Form Designer, except that the Run (!) button on the toolbar is disabled.There are several ways to use your own controls in the Form and Class Designers. First, the View Classes button (the one that looks like books) on the Form Controls toolbar lets you choose among various sets of classes. You can add your own class libraries using the Add option provided by that button. To make a particular library available all the time, register it using the Controls page of the Tools-Options dialog. These visual class libraries are registered and stored in the Windows Registry, under the entry HKEY_CURRENT_USER\ Software\ Microsoft\ VisualFoxPro\ <version number>\Options\VCXList. (Fill in 3.0, 5.0, 6.0, or 7.0 for <version number>.)We strongly recommend that you never use the base classes that come with Visual FoxPro. Create your own set of custom controls, consisting of all controls available in the default Form Control Toolbar but subclassed one level. Starting with VFP 6, such a set of controls is provided for you in the _base library found in the FFC subdirectory of your VFP home directory. We (and all the experts we know) recommend you do this because the base class controls supplied by Microsoft cannot be customized and cannot have custom properties or methods attached to them, whereas the subclassed group you have created (or the ones in _base) have that ability. (In fact, you'll probably want to subclass further to give you the ability to change things on an application level, as well.)One of the things we find frustrating about VFP is that you can't make changes across all the controls. If you want to use 24-point Haettenschweiler throughout all your development, you have to change it in each of your "base" classes (the ones we just told you to create). Of course, once you do so, you're done, and when you come to your senses, it's not too hard to change back to something reasonable again. However, we'd like it even more if all the VFP base classes derived from a single class, where this kind of change could be made once. We actually suspect that, internally, this may be true in VFP 6 and later, but we have no external evidence to back it up.Custom controls can be built from more than one component. The Reference section contains an example (under "Container") for Shapes, where two shapes and a text box are combined to form a thermometer control. These controls can be turned into a custom class as described above—just select all of the controls you want to combine, and then select "Save As Class". Visual FoxPro automatically dumps the selected controls into a Container control as part of saving them into a visual class library. As before, if you know what you want, you can just use the Class Designer in the first place, drop a Container on it, and add what you need. Remember to use your subclasses to create these complex objects—any base class object you used as part of a custom control cannot have additional properties or methods attached to it.Controls are the only means our users have to communicate with our applications. It's important to provide them with a rich, consistent and helpful set of tools with which to do their jobs. Visual FoxPro's powerful built-in controls, combined with the object-oriented structure that allows us to customize and build upon the capabilities of those controls, provides us with the means of delivering the tools our clients need.