Chapter 13
Forms and Controls
While some VFP applications do most of their work behind the scenes, forms are still the principal way for an application to communicate with users. VFP 9 offers a variety of ways to make forms more attractive and easier to use.
The bottom line in writing applications is providing some functionality. While some applications do that invisibly, most include at least some interaction with users. To those users, the application’s interface is the application. So anything that helps you provide an easier to use, more attractive user interface makes the people you write code for happier.
VFP 9 includes many changes aimed at the user interface. They range from significant new capabilities to minor tweaks. In this chapter, the changes are organized based on what piece of the interface they impact.
Controlling forms
Two major changes in VFP 9 affect forms as whole. First, VFP forms can now be docked. Second, a new property of controls makes it much easier to cope when a form is resized.
Docking user forms
VFP 7 introduced docking to the product, making a number of the IDE windows dockable, including the Command Window, the Property Sheet, and the View window. VFP 8 improved docking of those windows, providing programmatic control over docking and making docking persistent between sessions.
VFP 9 ups the ante significantly, making user-defined forms
dockable. This change means you can put dockable forms into your applications,
and all the developer tools
written in VFP code (like the Class Browser, the Toolbox, and so on) can be
docked.
(Only the Toolbox is configured as dockable out of the box, but you can modify
the
others to be dockable.)
Forms have three new properties and two new methods to
support docking. The properties are shown in Table 1. The methods are Dock, which docks and undocks the
form, and GetDockState, which lets you check on the status of a form.
Table 1. Forms have three new properties to support docking; two of them are the same properties as for toolbars.
|
Property |
Meaning |
|
DockPosition |
Indicates the current docking status of the form. Read-only. |
|
Dockable |
Specifies whether the form can be docked. |
|
Docked |
Indicates whether the form is currently docked. Read-only. |
In order to dock a form, it must support docking, as specified by the Dockable property. The property takes three values, shown in Table 2. You can change the property at run-time, for example, to let users decide whether or not a particular form is docked.
Table 2. A form can support docking or not. If it supports docking, at any given time, it’s either dockable or not.
|
Dockable setting |
Meaning |
|
0 |
The form cannot be docked (does not support docking). |
|
1 |
The form supports docking and is dockable now. |
|
2 |
The form supports docking, but is not dockable now. |
As with IDE windows, when a form is dockable (Dockable is set to 1 or 2), its context menu includes a Dockable item. When that item is checked, Dockable is set to 1; when it’s unchecked, Dockable is set to 2.
Turning on docking support for a form (that is, setting
Dockable to 1 or 2) affects quite
a few other properties. The complete list is included in the Help topic for the
Dockable property. Most immediately apparent is that HalfHeightCaption is set
to .T., which changes the appearance of the title bar. Figure 1 shows the impact of Dockable on the title
bar. Setting Dockable to 1 or 2 in the Property Sheet makes the form visible
before code in the Init method executes, instead of waiting for the Show
method. If you have code to manipulate the form
in the Init method, you may want to set Dockable in the Show method instead of
in the Property Sheet. In addition, when a form supports docking, the settings
of MaxHeight, MaxWidth, MinHeight, and MinWidth are ignored and BorderStyle is
set to 3-Sizable;
this means the user has free reign over the size and shape of your form, so
make sure it behaves appropriately.

Figure 1. The setting of Dockable affects many properties and determines the appearance of the title bar. The three forms here have Dockable set to 0, 1, and 2, from left to right.
Once a form supports docking and has docking turned on (Dockable=1), you can dock it interactively or programmatically.
Interactive docking of user forms works like docking of IDE forms—just drag the form to the border where you want to dock it. User forms can be docked with other user forms as well. Again, you use the same approach as with IDE forms. To tab dock two forms, drag the title bar of one onto the title bar of the other. To link dock two forms, drag one over the other until the outline of the dragged form changes to indicate docking. Interactive undocking of user forms is the same as for IDE windows, as well. In all cases except tab docking, drag the title of the form you want to undock. For tab docked forms, drag the tab of the form you want to undock. Of course, unchecking Dockable in the context menu of the form’s title bar immediately undocks the form. Use the Dock method to dock forms programmatically. A new parameter has been added to this method to support docking one form with another. The new syntax is:
Form.Dock( nPosition [, oForm ] )
The values for
nPosition are shown in Table 3. oForm is an object reference to another form.
When you include an oForm parameter, the two forms are either tab-docked or
link-docked, depending on the value of nPosition.
Table 3. The nPosition parameter of the Dock
method determines how the form
is docked.
|
nPosition |
Meaning |
|
-1 |
Undock the form |
|
0 |
Dock the form at the
top. |
|
1 |
Dock the form at the
left. |
|
2 |
Dock the form at the
right. |
|
3 |
Dock the form at the
bottom. |
|
4 |
Tab dock the
form with the specified form. |
![]()
Interactively, you can dock
user forms with any dockable windows, whether they are VFP forms, VFP tools
written in VFP (like the Toolbox or Class Browser), or IDE windows (like the
Command Window). Programmatically, though, you can dock user forms only with
other VFP forms, including
those for tools written in VFP. You can’t dock user forms with built-in
windows programmatically.
Of course, once you can
dock windows, you need a way to check whether they’re docked and, if so, to
what. The new GetDockState method fills an array with information about the
docking state of the form.
GetDockState accepts a
single parameter: the name of an existing array. If the form is dockable
(Dockable = 1), the array is sized appropriately and filled with data; in that
case, the method returns .T. If the form is not dockable (Dockable = 0 or 2), the
array is unchanged and the method returns .F.
![]()
The parameter to
GetDockState is unusual. You have to either pass the name of the array as a
string, or pass the array by reference.
The array GetDockState
creates has six columns. Table 4 shows the columns and their meanings. Although
GetDockState is closely related to the ADockState() function, it never returns
more than one row.
Table 4. The GetDockState method tells you about the docking status of a dockable form. It populates an array with these columns.
|
Column |
Contents |
|
1 |
Caption of the form that called the method. (Note that Help says this is the name of the form.) |
|
2 |
1 if the form is docked. 2 if the form is not docked. |
|
3 |
Docking position, using the values shown in Table 3. |
|
4 |
Caption of the form or window to which the form is docked. |
|
5 |
Object reference to the form that called the method, |
|
6 |
Object reference to the form to which the calling form is docked. If the form is docked to an IDE window, the empty string. |
![]()
GetDockState shares a confusing behavior with ADockState(). When forms or windows are tab-docked or link-docked, the first form or window in the group (the leftmost or top) shows as docked (column 2 = 1), but has –1 in the third column and columns 4 and 6 contain the empty string.
Anchoring controls
Ever since FoxPro
gained resizable windows, developers have been struggling with the question of
how to handle form content when a window is resized. Should controls be
resized? moved? left alone? How do we know what the user was trying to do by
resizing
the window?
Over the years, many
solutions have been proposed. What they all have in common (except for those
recommending you don’t let users resize forms) is that they involve a lot of
code, often requiring code for every control as well as the form itself. (You
can find one solution in the FoxPro Foundation classes, as _resizable in the
_controls.vcx class library.)
VFP 9 makes all that
code obsolete by adding the Anchor property to all visible controls. Anchor lets
you specify what happens to a control when the control’s container is resized.
The control can move, change size, or both, and the horizontal and vertical
directions are controlled independently.
When a user resizes a
form, you may not want all the controls to do the same thing. For example, if a
form gets both wider and taller, a textbox should probably get wider, but an
editbox should get larger in both directions. Any controls under such an
editbox need to move down, but controls above the editbox may want to stay
where they are. The Anchor property lets you consider all the possibilities and
set up your forms to behave in the way you think users will find most natural.
Anchor is an additive
property; you choose the values you want and add them together to form a single
value. Because the number of possibilities is large enough to make specifying
this value difficult, VFP 9 includes a tool (discussed in “The Anchor Editor”
later in this chapter) to help you.
Anchoring takes place
with respect to the four borders of the control’s container. For each, there
are three options:
·
the
control is anchored absolutely to that border, meaning the distance between the
specified edge of the control and that border remains constant;
·
the
control is anchored relatively to that border, meaning the distance between the
specified edge of the control and that border is changed to maintain the ratio
of that distance to the form’s size in that direction;
·
the
control is not anchored to that border, meaning a change to that border doesn’t
affect the size and position of the control.
Rather than anchoring
to each border, you can specify that the center of the control is anchored
relative to the borders, either horizontally or vertically, but the control
itself doesn’t change size. This is a good choice for controls that should move
in order to remain in a particular part of a form, such as buttons that should
always be in the lower right corner.
Table 5 shows the values you can add together for Anchor. Note that some of these values are mutually incompatible. The Help topic for Anchor lists the incompatibilities.
Table 5. To specify the Anchor property, add the values you want together.
|
Value |
Name |
Meaning |
|
0 |
Top Left |
Anchors the control to the top and left borders of its container, keeping the distance constant. This is the old, default behavior of controls. |
|
1 |
Top Absolute |
Anchors the control to the top border of its container, keeping the distance constant. |
|
2 |
Left Absolute |
Anchors the control to the left border of its container, keeping the distance constant. |
|
4 |
Bottom Absolute |
Anchors the control to the bottom border of its container, keeping the distance constant. |
|
8 |
Right Absolute |
Anchors the control to the right border of its container, keeping the distance constant. |
|
16 |
Top Relative |
Anchors the control to the top border of its container, keeping the distance relative to the original distance. |
|
32 |
Left Relative |
Anchors the control to the left border of its container, keeping the distance relative to the original distance. |
|
64 |
Bottom Relative |
Anchors the control to the bottom border of its container, keeping the distance relative to the original distance. |
|
128 |
Right Relative |
Anchors the control to the right border of its container, keeping the distance relative to the original distance. |
|
256 |
Horizontal Fixed Size |
Anchors the center of the control to the left and right borders of its container, keeping the control’s width constant. |
|
512 |
Vertical Fixed Size |
Anchors the center of the control to the top and bottom borders of its container, keeping the control’s height constant. |
Consider the form shown in Figure 2. The original form is shown on the left and the resized form on the right. When this form is resized, the size of the labels remains unchanged, the textboxes change size horizontally only, the editbox changes both horizontally and vertically, and the buttons remain centered horizontally. All the controls below the editbox move appropriately as the editbox changes size. Table 6 shows the settings used for the controls in this form.

Figure 2. When a form is resized, the behavior of controls depends on the type of control and the control’s position on the form. With Anchor, you can specify the behavior of each control separately.
Table 6. The form in Figure 2 demonstrates a
number of useful combinations
for Anchor.
|
Control |
Anchor |
Meaning |
|
lblName (Name label) lblAddress (Address label) |
0 |
Anchor absolutely to top left corner. Don't move or resize. |
|
txtName (Name textbox) |
10 |
Anchor absolutely to left and right sides, so resize horizontally. |
|
edtAddress (Address editbox) |
15 |
Anchor absolutely to all four borders. Resize both horizontally and vertically. |
|
lblCity (City label) lblState (State label) cboState (State combobox) |
4 |
Anchor absolutely to bottom border. Move vertically. |
|
txtCity (City textbox) |
14 |
Anchor absolutely to left, right, and bottom borders. Resize horizontally and move vertically. |
|
cmdSave (Save button) |
132 |
Anchor absolutely to bottom border and relatively to right border. Move horizontally to keep relative to original position. Move vertically. |
|
cmdCancel (Cancel button) |
36 |
Anchor absolutely to bottom border and relatively to left border. Move horizontally to keep relative to original position. Move vertically. |
![]()
The form in Figure 2 is included in the Developer Downloads for this chapter, available from www.hentzenwerke.com, as Anchoring.SCX. The Downloads also include States.DBF, which is used by the form.
VFP’s anchoring computations are based on the position of the control at the time the Anchor property is set. (No doubt what’s going on internally is that when Anchor is set, the Left, Top, Height, and Width properties for the control are stored somewhere.) Subsequent changes in code to the control’s position or size are ignored when resizing and repositioning due to resizing of the control’s container. To reset the base position for a control, set its Anchor property to 0, and then reset it to the desired value.
You may have noticed that setting Anchor to 3 seems to offer the same behavior as leaving the default Anchor value of 0. The difference is that with Anchor set to 3, subsequent changes to the control’s position or size are ignored when resizing, but with Anchor set to 0 they are not.
![]()
The Developer Downloads for this chapter, available from www.hentzenwerke.com, include
Anchor0vs3.SCX, a form that demonstrates the difference between Anchor=0 and Anchor=3.
Click the
Right or Down button on the form, and then resize the form. Watch the button
marked 3 jump back to its original position, while the button marked
0 stays put.
The Anchor Editor
Figuring out the right value for Anchor for each control on a form could get tedious fast. To make it simpler, the VFP team included a property editor (see Chapter 2, “Controlling the Properties Window”) for Anchor. This tool, called the Anchor Editor, lets you set Anchor visually; it’s shown in Figure 3. To use the Anchor Editor, click the Anchor property in the Property Sheet, and then click the ellipsis (…) button next to the textbox where you type values, as shown in Figure 4.
The Anchor Editor offers three ways to specify the setting you want. The diagram on the left lets you click or use the keyboard to set each of the four directions. Click between the parallel lines (called the “anchor bars”) or type the specified letter to toggle the setting for that border among no anchoring, absolute anchoring, and relative anchoring. As the setting changes, the diagram does as well. Black indicates relative anchoring, gray indicates absolute anchoring, and no color (the background color) indicates no anchoring.
The two checkboxes let you choose the two fixed size settings (512 and 256, respectively).
The dropdown list offers common combinations; the list of
choices changes based on
the control’s base class. For example, for a textbox, it includes “Resize
width,” “Move horizontally,” and “Move vertically,” but for an editbox, the
list includes “Resize height
and width,” “Resize width,” and “Resize height.” The list always includes “No
anchoring”
as a choice.

Figure 3. The Anchor Editor lets you specify the Anchor property visually. It offers several ways to specify the desired setting.

Figure 4. To use the Anchor Editor, click the ellipsis button when the Anchor property is selected.
In addition to shading the anchor bars, the Anchor Editor shows the current settings in two ways. The Border values box lists the current settings for each border for which anchoring is set. Also, the current numeric value is shown as the bottom left of the dialog.
The Anchor Editor also lets you examine the consequences of your current choices. Click the Sample button and a test form (titled “Anchor test form”) opens. It contains a control of the appropriate type (even using the Caption of your control) with its Anchor property set as currently specified in the Anchor Editor. Resize the form to see how the control behaves. The sample form is modal, so you have to close it to make changes.
When you close the Anchor Editor by clicking OK, the calculated value is saved to the Anchor property.
![]()
The Anchor Editor can work with multiple objects at once. If you select multiple controls, choose the Anchor property, and click the ellipsis button, the anchor settings you choose are applied to all of the selected controls. Be aware that when working with multiple controls, even several controls of the same base class, the dropdown for common settings is disabled.
Maximum form size
In VFP 8 and earlier, the Height and Width of a form were restricted to twice the screen resolution. VFP 9 raises the limit to 32,735 pixels for Height and 32,759 pixels for Width. We’re not sure why anyone would want to create a form that big, but you no longer have to worry about the screen resolution in creating large forms.
Displaying graphical elements
While FoxPro has included graphical elements (shapes, lines) since VFP 3, their use has been fairly restricted. VFP 9 introduces a number of changes that give you more control over the graphical elements on a form, including rotation of labels, the ability to create and rotate complex shapes, better handling of separators in toolbars, and the ability for labels to take on theme characteristics.
Rotating labels
Labels in VFP 9 have a new Rotation property that lets you change their position. Rotation accepts an integer value between 0 and 360. (If you specify decimals, they’re truncated.) Rotation takes place counter-clockwise. Figure 5 shows a label rotated 45 degrees.

Figure 5. Rotate labels by specifying the new Rotation property. Note that rotating a label doesn’t change the orientation of its Caption.
There are several caveats when rotating labels. First, be
aware that the text rotates as a whole. Rotation doesn’t provide a way to
create vertical text with one letter under the next,
for instance.
Second, you need to make the label large enough to contain the rotated text. In the form shown in Figure 5, the Height and Width of the label are both 100 pixels. (Think of the Height and Width properties as creating a “bounding box” for the label.”)
Third, a number of properties are applied only when Rotation is 0. They include AutoSize, Alignment, and WordWrap. (See the Help for the Rotation property for the complete list.)
You might expect Rotation=0 to give the same results as Rotation=360. However, when Rotation is greater than 0, the caption is centered in its bounding box, and then rotated. With Rotation=0, the caption obeys the Alignment setting, which doesn’t offer vertical position. As a result, the caption appears at the top of the bounding box.
![]()
The form shown in Figure 5 is included as Rotation.SCX in the Developer Downloads for this chapter, available at www.hentzenwerke.com.
Creating and rotating complex shapes
VFP has always included Line and Shape controls. However, the shapes you can create with these controls has been limited. Not surprisingly, the Line control has been limited to straight lines, with the LineSlant property determining their orientation (NW-SE or SW-NE). Shapes have offered a few more options. Depending on the setting of the Curvature property, you could have anything from a rectangle or square to an ellipse or circle, including a full range of rounded rectangles. But if you wanted any other kind of shape, you had to build it yourself using multiple Line or Shape controls; specifying colors or fill patterns for the result was difficult, if possible at all.
VFP 9 changes that. You can now create any shape, providing you know how to specify its vertices. Both Line and Shape include a new PolyPoints property you can use to specify an array of points that determine the line or shape displayed.
The array must be in scope when you run the form. The easiest
way to ensure that
is to use a property of the form or control. Specify the name of the array as
the value
for PolyPoints.
![]()
If the array is in scope and contains data at design-time, the actual shape or line is shown in the Form or Class Designer. Unfortunately, this is never the case when you use a form or control property for the array. In that case, you see the boundaries for the shape.
PolyPoints can handle both one-dimensional and
two-dimensional arrays. It simply
looks at the elements in order, treating all the odd-numbered elements as
x-coordinates
and all the even-numbered elements as y-coordinates. That said, it’s easiest if
you use a
two-column array. Then the first column provides the x-coordinates and the
second column gives y-coordinates.
The values you specify in PolyPoints are percentages of the space occupied by the bounding box of the Line or Shape. To have the vertices appear, the values should range from 0 to 100. Unlike the usual Cartesian coordinate system, however, the point (0,0) specifies the upper left corner and the point (100,100) indicates the lower right corner of the bounding box. You can actually specify points outside the box; when you do so, lines within the bounding box appear, but the object is clipped by the bounding box.
Keep in mind that using percentages means the actual rendering of the shape or line changes when the Height or Width properties of the Shape or Line change.
We think the easiest way to take advantage of these new capabilities is to subclass the Line and Shape base classes (or your first level subclasses of them) and add the necessary array property. Set PolyPoints to point to that array property. Then, create all your complex shapes and lines by subclassing or instantiating these subclasses. We also recommend adding a SetPoints method called from the Init method to handle population of the array.
![]()
The Developer Downloads for this chapter, available from www.hentzenwerke.com, include Shapes.VCX, a class library containing Line and Shape subclasses that use the new capabilities. The library includes abstract Line and Shape classes with the necessary properties and method, and concrete subclasses of those classes.
Figure 6 shows a form containing two Shape subclasses (the star and hexagon) and two Line subclasses (the W and teeth), as well as a label positioned over one of the shapes. The Resize button changes the height or width of each shape or line (the change is different for each—see the code for the details) to demonstrate the effect of Height and Width on the resulting shapes. Figure 7 shows the form after clicking the Resize button twice. (Note that the Wow! label contains code that keeps it centered over the star.)
You can change the points for a polygon or polyline even after the object has been displayed. For your changes to take effect, however, you have to call the object’s Refresh method.
Polylines offer an alternative to connecting all the points with straight lines. If you specify “S” or “s” for the LineSlant property (which until now has been limited to “\” and “/”), a Bezier curve is drawn instead. (A complete explanation of Bezier curves is beyond the scope of this book. Briefly, a Bezier curve is one method of approximately fitting a curve to a set of data points.) To use a Bezier curve, the number of points specified must be one more than a multiple of 3; the number of curves drawn is the multiplier of 3 used. That is, to have one curve, specify four points; for two curves, specify seven points; for three curves, ten, and so forth. If the number of points specified is not 3N+1 (for some positive value of N), the polyline doesn’t show up at all.
The right-hand polyline (the “teeth”) in the form in Figure 6 and Figure 7 has 10 points specified. The checkbox beneath it toggles the LineSlant property between “\” and “S”. Figure 8 shows the form after checking the Bezier checkbox.
Lines and shapes with an array specified for PolyPoints can be rotated using the new Rotation property. Figure 9 shows the form with all the lines and shapes (and the label) rotated 45 degrees.
![]()
The Rotation property is ignored for lines and shapes where
PolyPoints is
not specified. If you want to rotate ordinary lines and shapes, you need to
use PolyPoints and specify the vertices.
![]()
The form in Figures 6, 7, 8, and 9 is included as UseShapes.SCX
in
the Developer Downloads for this chapter, available from