Logo

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.

Page, PageFrame

Multi-page tabbed dialogs were one of the hottest things around when VFP was introduced. Now it seems they’re everywhere. Visual FoxPro’s version is called a PageFrame. It’s a container object that contains Pages. Pages are also containers and can contain any controls, even other PageFrames.

We do think page frames are pretty cool looking. We also think they’re terribly overused. Like so many of the other widgets in Windows, page frames are useful in dialogs. The Tools-Options dialog is the prime example of this, but many builders also make good use of page frames (and those guys are all built in Visual FoxPro). However, you can go too far with this. A page frame is heavily mouse-oriented—it has no place in heads-down data entry. (Neither do a lot of the other fancy controls, but that’s not what we’re talking about here.)

Use page frames where they make the resulting form clearer. The builders are a good example of this—while everything in the builder is related, each page contains a set of items that are closely related to each other.

Although tabbed interfaces are all the rage, you can also make tabless page frames. We can imagine using them for some of the things we used to do with multi-screen sets where all the screens occupied the same location.

PageFrame

Property

Value

Purpose

ActivePage

Numeric

The number of the page currently on top. This number is based on PageOrder, not creation order.

Objects

Collection

A COM collection containing references to the individual pages in the page frame. Also, has a Count property.

PageCount

Numeric

The number of pages in the page frame.

PageHeight, PageWidth

Numeric

The size of an individual page.

Pages

Collection

References to the individual pages in the page frame.

Tabs

Logical

Determines whether the individual pages have tabs at the top.

TabStretch

Numeric

Determines whether multiple rows of tabs are used when the tabs don't fit in a single row.

TabStyle

Numeric

Determines whether all tabs are the same width or each is sized to accommodate its Caption.

Event

Purpose

Moved

Fires when the page frame's position is changed programmatically.

Page

Property

Value

Purpose

ActiveControl

Object

Reference to the control on the page that has focus.

Caption

Character

The text on the page's tab. Can include a hotkey.

ControlCount

Numeric

The number of controls on the page.

Controls

Collection

References to the individual controls on the page.

Objects

Collection

A COM collection containing references to the controls on the page. Also has a Count property.

PageOrder

Numeric

The display position of this page in the page frame.

The documentation for VFP 5 and later says that Page has the KeyPreview property. (Page is listed in the Applies To list of the KeyPreview topic.) They did in VFP 3 (though it didn't do anything there). It was removed in VFP 5.

Page frames and pages refresh differently than other controls. When the Refresh method for a page frame is called, it calls only the Refresh method for the ActivePage. The other pages don’t get visually refreshed.

We can see why the Refresh methods don’t all fire up front—it could really bog things down. But why the heck don’t they get refreshed when they come to the top? How are your changes supposed to filter through?

Under the circumstances, we don’t see ourselves putting much code in the Refresh method for an individual page. We’ll use the page’s Activate method or the controls’ UIEnable events instead for things we want to be sure happen. We’ll also put This.REFRESH() in each page’s Activate method to make sure the controls get refreshed. (We’d love to handle all this with a nice subclass of PageFrame, but read on.)

Both Page and PageFrame have the MouseEnter and MouseLeave events added in VFP 7. These fire alternately, not together. That is, when you enter a page frame (from any direction), its MouseEnter event fires. But then, the page frame’s MouseLeave fires, and MouseEnter fires for the active page. Same thing as you exit – the page’s MouseLeave fires, then the page frame’s MouseEnter, then the page frame’s MouseLeave.

There’s one additional complication here. The tabs are considered to be part of the page frame, not the page. So if you enter over a tab, the page frame’s MouseEnter fires. It’s not until you leave the tab area and enter the page that the page frame’s MouseLeave and the page’s MouseEnter fire.

Okay, one more complication. When you change pages with the keyboard, MouseLeave for the old active page and MouseEnter for the new one do not automatically fire. It’s not until you move the mouse that those events occur.

Subclassing PageFrame is a real pain. You can create subclasses, but there are several nasty restrictions on them.

First, if you use the Class Designer, they always contain Pages—you can’t subclass Page and stick your subclass in a page frame. Page is one of the “half-classed” objects discussed in “OOP is Not an Accident.” In fact, you can create subclasses of Page in code, and even create subclasses of PageFrame that use your Page subclasses, but you can’t do it visually. More important, when you add pages to the page frame by upping PageCount, the new ones are based ON PAGE, not your subclass. There’s no way to tell a PageFrame subclass to always use a particular Page subclass. This is a serious limitation. However, FoxPro programmers, always willing to go the extra mile, have found a solution. You can put code in the page frame class’s Init method to remove the base class pages and substitute your custom pages. You can even set all the properties as they were in the base pages, so that you can do your design work in the Form Designer. Going this way, though, you lose the ability to add custom code to the individual pages, since there’s no way to add code to an object at runtime.

Second, when you put your subclassed page frame in a form in the Form Designer, you can’t reduce the number of pages—the only way to get rid of a page is to RemoveObject at runtime.

No big deal, you say. You’ll just use a subclass with no pages and change PageCount in the Form Designer. But then you lose most of the benefits of subclassing in the first place—you can’t define your default page and start out with it.

Next problem. If you leave the pages on the page frame, when you drop the class onto a form or create an instance, you can’t change the names of the individual pages.

Fundamentally, there’s no good way to create a single subclass of PageFrame and use it wherever page frames are called for. Your best choices are to put a lot of code in your PageFrame subclass to modify pages as they’re instantiated or to subclass Page in code and use AddObject at runtime to add your subclass to the page frame. We’re not thrilled with either choice, but it’s looking more and more like Microsoft isn’t going to get around to making Page a first-class citizen.

Early versions of VFP had some problems dealing with changes to a page's Name after you've started messing with it. All the code to set page properties is stored in the Properties memo of the page frame that contains it. In earlier versions, when you set some properties for the page, and then changed the page's name, some of the property assignments used the original default name (Page1 or whatever) while some used the new name.

Example

* All the changes shown here would be made
* in the Property Sheet.
* Set a PageFrame to have 3 pages.
* With the PageFrame selected:
PageCount = 3

* Set a page to have white on blue text
* and to have a tab of "Sky".
* With the page selected:
BackColor = RGB(0,0,255)
ForeColor = RGB(255,255,255)
Caption = "Sky"

See Also

ActiveControl, ActivePage, Caption, ControlCount, Controls, KeyPreview, MouseEnter, MouseLeave, Moved, Objects, PageCount, PageHeight, PageOrder, Pages, PageWidth, Tabs, TabStretch, TabStyle, UIEnable