Property Pages, Changes, and Persistence

Although interfaces such as IDispatch and IDataObject can provide access to an object's data programmatically, there are occasions when the user would like to work directly with an object's properties. OLE's mechanism for doing just this—the mechanism uses a tabbed-dialog user interface, as shown in Figure 1-13 on the following page—is called Property Pages, which was introduced initially with OLE Controls. Each separate tab in the dialog box is a property page, and each page is managed by a separate object, with its own CLSID, that implements the interface, IPropertyPage (or IPropertyPage2). An object that supports property pages implements ISpecifyPropertyPages, which supplies a list of property page CLSIDs that it would like displayed in the dialog.

A property page isn't all that useful by itself, so whatever component wants to show the pages creates a property frame (which is the dialog box) and hands that frame all the CLSIDs of all the property pages to display, along with the IUnknown pointers to all the objects that are being affected by changes in those property pages. When the user clicks on the Apply Now button in the dialog box to apply the changes to the affected objects, the frame notifies the current property page to notify each object in turn.

Figure 1-13.

The user interface for property pages involves a tabbed dialog box in which each tab is a separate property page.

Property pages are extremely useful for things like controls, and they will find more use for various user interface elements in future versions of Windows. As a technology, they are rather general purpose, so you'll likely find many uses for them.

Regardless of how a property changes, either programmatically or through a property page UI, some clients might be interested in knowing when certain properties (identified with a dispID) change. An object that supports notification of property changes supplies a connection point for the interface IPropertyNotifySink. Interested clients implement this interface and hand it to the object through the latter's connection point.

This interface handles two notifications. The first is that the property has actually changed value, in response to which the client can retrieve the new value. The second notification tells the client that the property is about to change, which allows the client to prevent the change altogether or perhaps to implement some sort of read-only behavior. Properties that support the first type of notification are called bindable properties, and those of the second type are request edit properties.

The final consideration for properties is a persistent storage method called property sets, by which a component can write properties into a stream in a self-describing manner. The actual structure of a property set starts with a header that describes the offsets in the stream of each property therein, and at each offset is a header describing the type of property and the offset of the actual data elsewhere in the stream. This structure is very flexible. It allows a component to write only those properties it has and allows another component or client to robustly read only the properties that are there. There are no expectations about the existence of any property—if it's in the stream, it's there completely.

Property pages, property change notification, and property sets are covered in Chapter 16.