Introduction to Controls


This document is reprinted from the Microsoft DevCon 95 Speaker materials and is provided "as-is." This document and any associated demo files were created using Visual FoxPro 3.0. Some features discussed may have changed in Visual FoxPro 5.0.

Lisa Slater Nicholls
SoftSpoken

Overview

Visual FoxPro parades a fabulous series of new and improved controls, each one marching its own impressive regiment of properties, events, and methods through the Properties window. To get right to the business of designing effective and attractive interfaces, these notes will highlight features of the control classes that help you:

Folks, we’re fortunate that it’s easy and fun to play with controls. You’re going to have to do a lot of experimenting before you settle on a work style and an interface approach that satisfies you in Visual FoxPro. Getting very quick feedback and satisfying rewards is vital to this process of experimenting.

Don’t be afraid to play around, and be prepared to throw away a very large percentage of your early efforts, even though they work. As you play, you’ll discover more attractive and efficient ways to provide the same things.

These notes were developed for Visual FoxPro Power ToolKit, published by Ventana Press, and first presented at the 1995 Microsoft FoxPro Developers’ Conference. They focus on avenues of inquiry and discovery that I think you’ll find fruitful, as well as some “signposts” that should help you avoid blind alleys along the way. These are the product of my own experiments.

What are controls, anyway?

Controls are officially defined as “the primary method of user interaction” in the Visual FoxPro beta documentation. That is, they allow the user to communicate his or her intentions, information, and requests to the program. They allow the application to communicate information and instructions, as well as to request more information and instructions, back to the user.

I like this definition of controls better than the Apple Human Interface Guidelines’ description of controls as “...graphic objects that, when manipulated with the mouse, cause instant action with visible or audible results.” The HIG statement gives you a sense that the application always reacts to a user-initiated action. By contrast, the Visual FoxPro definition allows us to include interactions that may be initiated on either side. A timer control, for example, may initiate an interaction and the application expects the user to respond to it.

Even within the Visual FoxPro documentation, there is some confusion about what can be included in the definition of a control. Some controls may not directly interact with the user at all, but rather contain other controls that do. For example, an option button group (sometimes called a set of radio buttons) is not something the user directly uses. It is a container for the actual buttons the user clicks — yet it is still a kind of control.

In FoxPro, we always want our definitions to be as inclusive as possible, to give us room to flex the interface in response to user requirements. No matter what platform you work on, even within the structured Mac interface, database management presents perhaps the most complex, and certainly the most varied, interactions of any application category.

Because there are so many different kinds of communication required in applications, there are lots of different classes of Visual FoxPro controls. You make design decisions about which controls to use, following interface standards.

You probably already know the broad outlines of these rules already, without giving them a second thought. You use option buttons to make a choice between a set of separate-but-equal choices of which exactly one is valid. You use command buttons to indicate that the program will carry out an action in immediate response to the user’s instructions. And so on, ad interface!

At some points in FoxPro, you’ve probably been stymied by your desire to implement a control that responds in a specific way, or with certain visual cues. To answer some of these needs, Visual FoxPro adds new controls, like the combo box, and new control functionality, like multi-select lists. It also gives you unparallelled ability to manipulate the visual features and behavior of all controls.

You will quickly see how to add the new controls into your existing interfaces. It’s the manipulation of controls to do your bidding that is truly interesting and truly new.

We’ll tackle a simple example, and see where it leads.

Where do you start?

As always in FoxPro, there are two ways to define and assert these program elements in your application: you can write code to describe them, or you can use a design tool to create them. As always in FoxPro, you should use the design tools in preference to writing code. You may have thought you could get away with learning all the bizarre @... GET syntax added in FoxPro 2.x, but wait until you get a look at all the new syntax in Visual FoxPro 3.0. If this doesn’t convince you to give up hand coding, nothing will!

Start by realizing that the Form Designer, in which you visually create controls, is not the Screen Builder. For one thing, there isn’t any generated code, no .SPR. For another, the Form Designer is paired with a Class Designer. They look almost exactly the same, but even our simplest example will quickly teach you why you sometimes use one and sometimes the other.

IMPORTANT CAVEAT: I have another good reason for concentrating on the Visual-Designer method of creating controls at this time: as I write these notes (during the Beta period), the Visual FoxPro syntax is still being created. Please read my instructions with the knowledge that I want to teach you techniques, not syntax — especially because the syntax may not exactly match the shipping product.

Consider this fairly standard requirement: a spinner that increments or decrements date values. To create it, open the Form Designer. (Choose File... New... Form from the menu.)

Design a Control

You see a form (window) as a frame within the Form Designer. From the Form Controls Toolbar, pick the Spinner button, which looks exactly the way it used to look on the side of the Screen Builder. If you don’t see the Form Controls Toolbar, you can bring it up using the View menu pad’s Toolbar dialog,or the Form Design Toolbar’s hammer-and-wrench button. Figure 1, below, shows you the Form Design Toolbar docked under the menu, and the Controls Toolbar floating to the left.

You can size and place the spinner on the form as you would in the Screen Builder in FoxPro 2.x. How do you affect the spinner’s behavior and appearance? Figure 1 shows you the Properties window (I’ve right-clicked the spinner control to bring it up), which is the main dialog-component of the Form Design tool. It centralizes your access to the attributes you can adjust for the selected form element. You can move from the spinner control to adjust the attributes of other parts of this form, using the drop-down list on the upper left.

Figure 1. The Form Designer with Form Design toolbar docked, Controls toolbar to the left, and Properties window on top. Note the pushpin depressed in the Properties window, indicating that it’s “always on top”.

Take some time to scroll through the attributes you find listed in the Properties window. You’ll find a different set if you switch to looking at the attributes of the Form itself, rather than the Spinner since (obviously) these two types of controls don’t perform the same tasks.

Control Characteristics

You’ll find that the attributes are classified using a tabbed dialog in the Properties window; I find myself using the All tab most of the time, to familiarize myself with attributes I might not notice otherwise if I restricted myself to one of the other pages. Besides, I find it difficult to know exactly which tabbed page will contain a given attribute; at least with the All page open, I’m sure of finding it!

Besides the classification system afforded by the tab pages, all the entries in the Properties window also fall into one of three basic categories:

Doubleclick on the value showing for a Property to adjust it; logical values will toggle, color values will bring up a GETCOLOR() dialog, and so on. Doubleclick on the description of an Event or Method, and you get a code window instead. You may find it confusing that entries in the Properties window (such as DragOver and Error, in the figure) may be both events and methods, but if you continue to think of the Event as the outside stimulus and the Method as the control’s response to that stimulus, you should be okay. I’m not sure that these are orthodox OOP definitions, but OOP theorists disagree on the distinctions between methods and events (if they agree that there are any distinctions at all). I’m concerned with practice, not theory, here.

Continuing to create your spinner, you might think that you should be able to scroll through the Properties window and find some entry such as SpinnerValueType, in which you could specify Date. This doesn’t happen to be a native ability of Spinners in Visual FoxPro. It’s a snap to achieve, however. Simply resize the text entry portion of the Spinner until you can’t see it, and add a Label control next to it. I used this Label Caption:

=DTOC(DATE())

Note the change of type to character, suitable for the Caption property, and the = sign indicating to the Properties window that this is an expression. (If you type a literal character value, as is quite common for Captions, you type it without the quotation marks; that’s why the = sign is necessary for expressions.)

Then I instructed the Label to change its caption in response to events that occurred to the Spinner, as follows:

* Doubleclick on the Spinner’s DownClick Event
* in the Properties window, 
* or rightclick the Spinner in the Form Layout,
* and choose Code, using the righthand dropdown list
* in the Code window to get to the DownClick Event.
* Enter this code in the code window:
THISFORM.Label1.Caption = ;
	DTOC(CTOD(THISFORM.Label1.Caption)-1)

* Enter this code in the UpClick code window:
THISFORM.Label1.Caption = ;
	DTOC(CTOD(THISFORM.Label1.Caption)+1)
Telling controls what to do, and getting controls to talk to each other

Did you notice how I referenced the controls in the code that changed the Label’s caption in the Spinner method? Here are the general rules:

Execute the form and check your results

Back to the Spinner, you’ve written two lines of code, each in a different code window.When you save the UpClick and DownClick methods, you see that the appropriate event now shows the description (User Procedure), indicating that there are methods attached to these events.

It’s that simple. If you want to see the results, just DO the FORM, using the Program menu option, or executing this command in the Command Window. You’ll see a result something like Figure 2, which will give you the results you expect when you use the Spinner.

Figure 2. SPINDATE.SCX on your source disk.

You now know how to adjust properties dynamically at runtime. To change almost any property (a few are not available at runtime, but almost all are), you reference it the same way.

While you’re admiring your work, you probably notice that your Command window is active at the same time as you can use the Spinner in the form. You may want to execute a second copy of the form (DO FORM <name> a second time). You can even reference the objects in the forms dynamically in the Command window to change their properties interactively. Following the rules above, you use the Container that is actually common to all FoxPro objects, _SCREEN, and then the special keyword that indicates the active form, as you can see in Figure 3.

Figure 3. Interactively adjusting properties.

I could have used a command in the Command window to change the caption property of one of the forms, for example, so that each form had a different title.

You’ll find yourself experimenting with properties, changing them interactively in the Command window and watching the effects; it’s a wonderful learning device. Here’s another neat example using the SpinDate form: in the beta build I’m working with, I see an annoying flash caused by the cursor in the hidden text-part of the spinner control. The default value for the spinner’s alignment property is 1 (Right), because the value is assumed to be numeric type. To eliminate this flash in SPINDATE.SCX, I’ve changed the Spinner1.Alignment value to 0 (Left), so the cursor is hidden underneath the label when the spinner has focus. But if you’ve been following my instructions above, however, you should see the flash in your own sample form. You can use the following code in the Command window to fix it:

_SCREEN.ACTIVEFORM.Spinner1.Alignment = 0  && Left Alignment

In the source code you receive with these notes, I’ve taken this idea a little further by creating a button with a Click event that executes the code DO My_Test. I write My_test.PRGs on the fly, in the Command window, press the button on the form, and check the results. The button is designed to “disappear” itself when there is no My_TEST.PRG or .APP available, so I don’t even have to remove it from the finished screen.

You can use the keyword ACTIVECONTROL interactively, as well, along with ACTIVEFORM. But sometimes you need to refer to one form out of many during these tests, or in your applications. One way is to DO the FORM <.SCX name> NAME <alias>. Now you can refer to this container’s controls by saying <formalias>.<controlname>.

A comparison with FoxPro 2.x techniques

I used a Label because I didn’t want text entry in my date— you could use a Text Box instead, if the user should be able to type in a date.

Make sure to notice that the Text Box has a Value property the Spinner will now adjust (instead of the Label’s Caption property).

Think of Labels as fulfilling the function of Refreshable SAYs — it’s tons easier, and much more efficient, to specify a new Caption than to SHOW GETS or create a GET WHEN .F. to refresh what the user sees.

However, there is a crucial difference between Labels and Text Boxes which may cause you to use a Text Box occasionally even when the user does not directly enter data: the latter can be bound to data, while the former cannot. Whether a control stands on its own or whether its value directly influences a field in your tables is a critical design decision you will make as you create your forms.

Don’t be afraid of what we used to call “direct READs” versus READing to memory variables; Visual FoxPro’s new system of data buffering and transactions will change the factors you weigh in this decision (besides, you might bind the controls to a cursor with which you updated your table later).

If you want Label-like behavior and yet want to take the displayed data from a table, simply use a TextBox with its ReadOnly property set to .T. This is very much like the old GET WHEN .F., but you’ll find it much easier to get the colors to synch up:

THIS.BackColor = THISFORM.BackColor
THIS.ForeColor = THISFORM.ForeColor

The form itself has a background and foreground color. Strictly speaking this is not necessarily the same as the background and foreground color of labels (the form’s colors affect elements you create on the form using its Draw method). You could always set the textbox’s colors using THISFORM.<somelabelname>.BackColor and .ForeColor instead. It’s important to realize that labels and other controls don’t inherit characteristics from their parent containers, just because they are housed in the containers. Instead, they inherit from a heirarchy of classes, starting from a base of the classes Visual FoxPro provides natively. In the case of a Label control, its base class is (not surprisingly), the Label class.

Abstract from your new control to your own new classes

Simple as this date-spinner idea is, it can easily be abstracted to a generally useful tool that isn’t available as one of the control types in native Visual FoxPro. Selecting both the Spinner and the Label at once, use the File menu to Save the two controls together As Class. You will be asked for a classname, and a visual class library filename, with the extension VCX. Now you’ll be able to edit this new class (based on Visual FoxPro’s internal Container.class) in the Class Designer.

You can think of the two grouped controls, in their new class, as an extremely sophisticated form of grouping controls, which we could do in the Screen Builder in FoxPro 2.x. They have become a single, portable, application element. You can subclass this new tool to provide different sizes, fonts, or other attributes without rewriting the underlying code.

Figure 4 (below) shows you the Class Designer in action. You can also see the way the Project Manager coordinates references to VCX files and the various class definitions they contain within them. (The Project Manager is docked, with its ALL tab torn off for easy reference, the way I prefer to work with it.

Notice that you can access your class using the button on the Controls toolbar at the top right in the figure. You can drop datespinners onto forms just as you drop the native controls onto forms. They all have the behavior defined in their class, and they will partake of any improvements you make to the underlying class in the Class Designer.

In the same way, I discovered a need for the test button I’ve described earlier, and created a test button class. When you look at this button in the version on your source disk, pay special attention to its error handling. When you write code for error handling within a control, you find that it applies to any procedures run from that control, including (in this case) the My_Test.PRG or .APP the button’s click event runs. This is extraordinarily convenient. Controls can, and should, have errorhandlers tailored to their special purposes.

The error method is passed certain parameters by Visual FoxPro. When you bring up the error method code window, you’ll find these parameters are already entered for you. This applies to any event that corresponds to a method with required parameters, such as the MouseDown event.

Figure 4 shows you all the code associated with the Test button. Note that when Visual FoxPro passes the cMethod parameter to the Error method, it properly distinguishes between the test UDF and an error in the method itself. This is useful because I often make typing mistakes in the little My_Test.PRGs. However, it has more general implications: you may find yourself writing your code as little PRGs while debugging, with already-proven code in the method. You easily test variations, moving the new bit of code to the method when you’re satisfied, without having to re-MODIFY the FORM or CLASS for each iteration.

Figure 4. Design Classes and incorporate them into your projects.

Figure 5. Class CmdTestMy_UDF

Controls as building blocks

Think of controls as something like graphics primitives. You aren’t limited by the simple line, box, and oval you find in a drawing program, because you can combine them to form other shapes. The control-analogy to the complex shapes you can build in a drawing program are the classes you can define, built out of many different types of controls. These classes will often have the Base Class of Form, and they will represent common data entry formats you use repeatedly in your programs.

Get your building block set organized...

You can set a template form or formset, representing the pattern of attributes on which you want to base all your new screens, something like Stationery on the Macintosh or a WinWord template (.DOT). This is almost like being able to define a new Form BaseClass. When you CREATE a FORM, up comes your set of definitions, not the blank form that Visual FoxPro would usually provide.

From the Command window, use CREATE FORM <name> DEFAULT to override your currently-registered template.

On the other side from templates, which start you off with fixed definitions, you may want to have certain attributes differ for each screen you design — for example, the Caption property for Labels. You’ll soon get tired of hunting through the Properties window to adjust the same items for every control. This is where Visual FoxPro’s builders come in. You designate a program as the builder using the system variable _BUILDER, Visual FoxPro passes a reference to the object you’re creating to this program, and only you and your _BUILDER know what happens next!

You could design a custom dialog to come up, requesting information on those properties you wish to adjust based on the object’s class and requirements, or you could run a procedure to automatically configure the new control based on some other information in your development environment. In the Builder, you then use the object reference and set property values just as if you were doing it in a normal application.

The Builder can perform these miracles because the form and controls you’re designing are “live” in the Form Designer. They have the same attributes they will have at runtime. There is an important corollary to this fact: don’t choose attributes that will make it difficult for you to see what you’re doing in the Form Designer! For example, if you want a form to be minimized when it appears at runtime, don’t set the form’s WindowType property to minimize it until you’re finished with the rest of your work. You might prefer to minimize it (using the same property of course) only at runtime, in fact, perhaps in the form’s Init event.

... and get more building blocks when you need them

Just as you don’t try to build a complex drawing out of graphics primitives, but rather import an image as a single unit, at a certain point you don’t want to fuss with single labels, spinners, and other controls to build a complex set of interactions with the user. Fortunately, Visual FoxPro’s system of controls supports an analogy to the imported bitmap: OLE controls. You’ll find that they follow much the same rules of behavior as the native set, although they often have properties and methods that are specific to them (in Figure 6, you see the Properties dialog you get if you rightclick on an Outline control).

Figure 6. OLE controls have control-specific properties.

Classes that you create — and even instances of controls that you’ve added to regular forms in the Form Designer — can have custom properties and methods, too. You can reference them just you reference the native set.

For example I created a class called STICKY to allow me to stick notes on forms. It is composed of a label control (lblStickyLogo), which I use to “stamp” the sticky with a signature design on top, and an EditBox, (edtStickyNotes), into which I enter the text. The code for this class includes two custom properties. StickyResizable tells me whether the Sticky should automatically configure itself to fit the amount of text typed in. This is often a desirable feature, but I set StickyResizable to .F. when I’m using the Stickies as placeholders for other controls, showing their purpose and approximate screen real estate, while I’m prototyping a form. I use the other property, RightMargin, to change the size of the Edit Box relative to the Sticky container. In this case, I created the property because I wasn’t sure what “margin” would look good when I came up with this idea. Rather than subclassing with different values for this property, as I do with Resizable, I’m likely to reset RightMargin in the base Sticky class, to affect all Stickies, once I decide how I want them to look.

I use both properties within the Sticky’s Refresh method, which sizes the Sticky container. To call this method myself, whenever the requirements of the Sticky’s parent container (usually a form) have changed, I use the following syntax:

THIS.Refresh()

If I added custom methods to the Sticky class, I would reference them the same way in code. The parentheses aren’t strictly necessary, but I use them to distinguish methods from properties.

I’ve omitted the Sticky class from your code examples, because right now it’s a casualty of version changes. However, you’ll find that my expanded DateSpinner class offers many of the same techniques. You will want to examine its Protected SynchSize() method to see how the sizing is done.

Continue from here...

Be sure to go carefully through my code samples for additional “goodies”. The associated README.DBF contains more advice, some specific to each sample and the controls it features, and some updated last-minute thoughts of a more general nature.

When you get a good look at some of the cool properties and capabilities of the new control set — pageframes, grids, and beefed-up lists among them — you’ll realize that I could have written this discussion and generated excitement just by listing them and showing you a couple of whiz-bang examples. Even before you customize a single control, you’ll find you have plenty to play with. I’ve included some of the toys I’ve been playing in my source code.

You can DO FORM LSN_DEMO, or DO LSN_DEMO, to receive a "tour" of the files, including a look at the README.DBF I wrote to describe them all for you (see Figure 7 below).

You can also DO FORM <name> with any of the SCX files here -- they'll all run, although a few of them (FILEFIND and FILESORT) are really subsidiary modal dialogs that won't do much on their own. (These will eventually become classes, of course.)

There isn't anything you can mess up by executing any of the programs out of order -- in fact, there wasn't any need for the LSN Project or the LSN_DEMO.APP at all, I just threw them in for convenience! The .APP will provide a little structure, to help you open up the various files as you decide to look at their code. It will also give you a good look at the modeless event system within which you now work — since LSN_DEMO will open up the various examples and continue running, with a fully-functioning Command window inviting you to test the examples yourself, with additional parameters, as you go.

Figure 7. Investigate the code samples in LSN.PJX through the demo APP, or look at the forms on their own...

You see, I don’t think that your learning curve and the difficulties you’ll encounter center around finding out about the new controls and their myriad properties. The techniques to reference them and develop with them are the real trick, and they will remain the same — whether you’re using base class controls, or subclass to new controls of your own, or OLE objects is immaterial.

I hope that these notes have given you the help you need to begin experimenting with them all.