Most likely, you've already seen the user interface known as the tabbed dialog box or property sheet, a dialog box containing one or more individual property pages, in which each page itself works as a single dialog box. The collection of pages in a single place allows the end user to work with different properties of one kind or another without having to skip around between different, singular dialog boxes. In a property sheet, each page is displayed as an individual tab; clicking on a tab brings the various controls on that page to the foreground.
You'll see property sheets employed quite often in the Windows 95 user interface, not only in applications but also in the system shell and in development tools. For example, let's say I'm using a programming tool to create a push-button control on a form. When I ask to view the properties of that button, I get a Button Control Properties property sheet, as shown in Figure 16-1. The General page is currently displayed. I can use this page to set the button's caption. If I click the Fonts tab, I see a different page, shown in Figure 16-2 on the following page. And if I click on the Colors tab, that page comes to the foreground, as shown in Figure 16-3 on the following page. So within the context of the button control object, I can manipulate its different groups of related properties within a single dialog box. Keep in mind that in all these figures, the OK, Cancel, Apply Now, and Help buttons—as well as the labeled tabs—are owned by the frame dialog. All other controls belong to the pages themselves.
Figure 16-1.
A General property page in a Button Control Properties property sheet.
Usability testing has shown that end users work well with the grouping of related sets of properties in the same top-level dialog box. One dialog box with multiple pages reinforces the fact that all the properties apply to the same underlying object, and in many ways, factoring properties in a user interface is much like factoring an object's functionality into multiple programmatic interfaces, as we've seen throughout this book.
Figure 16-2.
A Fonts property page in a Button Control Properties property sheet.
Figure 16-3.
A Colors property page in a Button Control Properties property sheet.
Windows 95 introduced the capability to create property sheets as part of the system API, allowing applications to create this user interface for their own properties. In general, each property sheet represents some conceptual object as the user sees it. For example, the Windows 95 Control Panel is a conceptual system-settings object that has groups of properties for the display, device drivers, peripherals, the network, and so on. Within the property sheet for these system settings, each group of related settings has its own page.
In a component integration environment such as OLE, we want OLE components to be able to specify which property pages they want displayed for themselves and to be able to supply the implementation of those pages. This allows a client to display the appropriate property sheet for an object being maintained in that client—or even for multiple objects simultaneously. The Windows 95 API for creating property sheets is not wholly sufficient for OLE's purposes because that API is oriented toward a single application displaying its own properties. In OLE, we need a client to display the properties of any group of objects and have changes in the property pages applied directly to those objects without the client's intervention.
The group of technologies introduced with OLE Controls includes a number of new interfaces and an API function that creates and manages component-specific property sheets. We call this set of interfaces and functions the OLE Property Pages technology. This technology is oriented toward a client that has IUnknown pointers for one or more objects and that wants to display the appropriate property sheet for any subset of those objects. (Client here also applies to an object that wants to display its own property pages in response to a programmatic request such as a method call to its primary dispinterface.) In any case, the property sheet manages to communicate property changes in the user interface to the objects being affected. The mechanism that makes this possible between arbitrary objects involves four parts:
Each object specifies which property pages should appear in its own property sheet through the interface ISpecifyPropertyPages, where each property page is identified by its own CLSID. This interface is necessary only to support the display of a property sheet within an object's external client—objects that want exclusive control over the display of properties should not implement this interface.
Whoever wants to display the property sheet (the client or the object) calls the OLE API function OleCreatePropertyFrame (or OleCreatePropertyFrameIndirect), passing an array of property page CLSIDs along with an array of IUnknown pointers (one for each affected object). This function creates the modal dialog frame and manages the individual property page objects themselves until the user closes the dialog box.
Each property page is an in-process object that implements IPropertyPage. Through this interface, the frame tells each page when to show or hide itself as the user selects different tabs. In addition, the frame passes the client's array of IUnknown pointers to each page so that those pages can send changes directly to the affected objects.
Each tab in the frame dialog is a page site that appears to each property page as the interface IPropertyPageSite. The pages use IPropertyPageSite to retrieve information about the property sheet as a whole.
The relationship between these parts is illustrated in Figure 16-4. Be aware that property changes are communicated directly from the property pages to the affected objects; they require no interaction on behalf of whoever invokes the dialog box. Because each object specifies exactly which property pages to display, those pages expect to communicate changes through specific interfaces or dispinterfaces. In some cases, having a standard property page for common properties (such as Fonts and Colors) makes sense. A standard page expects objects to support standard dispIDs for related properties through their IDispatch interfaces.
Figure 16-4.
The architecture of OLE Property Pages.
The following four sections take a closer look at each part of OLE Property Pages. In addition, we'll look at a few other interfaces, namely IPerPropertyBrowsing and IPropertyPage2, that provide for more specific user interface access to an object's properties.