Chapter 11 Viewable Objects and the Data Cache

In real estate, you can distinguish between properties with a view and properties without one (or, say, with a view of the hind end of a grocery store). You can say the same thing about vacation destinations. There's a big difference between places with grand views of natural wonders and those with a view of the backside of a grocery store.

Now, we've all received those postcards from so-called friends—postcards that show the most fabulous views in the world along with the note, "Wish you were here." Yeah, right. While a picture is worth a thousand words, a picture still doesn't come close to being there. Small, two-dimensional photographs have nowhere near the latitude for color and contrast that can be seen with the human eye. They're just not the same.

In Chapter 10, I mentioned that most objects have information of some kind. That information is commonly called the object's properties. Through the IDataObject interface, you can ask an object for those properties directly, or you can ask for a graphical rendering of that data. As far as seeing the "real thing" goes, these graphical renderings are akin to asking friends for photographs of the view from their own property. Having a data object send you a new rendering with a data change notification is somewhat like a friend sending you a postcard.

These renderings are, of course, very useful—a consumer of data will often incorporate such pictures into its own user interface—but they do have some limitations, just as a photograph is limited in comparison to the human eye. First of all, you can work with only limited graphical formats, such as CF_METAFILEPICT, CF_BITMAP, CF_DIB, and CF_TIFF. While you can create any image you want in a bitmap or TIFF format, these formats are typically large and bulky and consume a lot of memory and disk space. Metafiles are more efficient with respect to storage, but they also have their limitations because certain graphical operations cannot be written into a metafile format. In all of these cases, it is also difficult to fully exploit the target device. While a data object can create graphics appropriate to the device (that is, for the colors and resolution available), it cannot use the services of that device directly. For example, a data object that knows about PostScript and is asked to render itself for a PostScript device cannot simply send PostScript commands directly to the device.

To accomplish direct rendering to a device—and to optimize the quality of the output—the object itself must have access to the device context, the Windows hDC for whatever screen or printer the consumer (or client) wants to show the object's image. To that end, OLE defines the interfaces named IViewObject and IViewObject2, through which an object tells its clients that it can render directly to a device. The primary member of these interfaces, named Draw, takes an hDC as an argument, giving what we called the viewable object direct access to the device. So instead of a client asking a data object for a graphical rendering, that client can ask the object to draw itself here. Instead of asking the object for a photograph, the client asks to see the real thing with its own eyes, without limitations in detail, quality, and so on.

IViewObject and IViewObject2 are the primary topics of this chapter. Besides the Draw function, these interfaces express the ability to freeze and unfreeze an object's view (so as to stabilize it during printing), expose the colors used in the rendering, set the size of the rendering, and allow a client to request notifications from the viewable object through IAdviseSink::OnViewChange. This kind of notification allows a client to redraw the object only when the view has changed (as opposed to a data change, which might not affect the view) and keeps the client's image up to date with the object.

All of this is not to say that graphical renderings in the metafile or bitmap formats are entirely useless. One look around will tell you that photographs and other static images are highly valuable and have many uses. In fact, having a client cache one or more object images in a document or other such file is often very useful because then the object need not be present to view its data. This is an important consideration. A client doesn't want its own presentation to be altogether dependent on the presence of objects. If that client is run on a machine without those objects, it can't do anything more than show a bunch of ugly gray boxes saying, "There was once an interesting picture here; please use your imagination." Can you hear your customer support phones ringing off the hook?

A second case in which a cache is important occurs when obtaining a new rendering for an object would otherwise be very expensive, with or without IViewObject2 in the picture. Let's say that a client asks a data object for a metafile rendering that contains something like 50,000 distinct graphical elements—a large rendering indeed. Let's also say that the object in question executes from a local server that takes at least 20 seconds to run. Without a cache, the client would have to continually run this huge local server and have it repeatedly render this large graphic whenever that client wanted to display or even repaint the image.

To address these concerns, OLE provides the implementation of a service called the data cache, whose object implements the interfaces named IOleCache2 (with the base interface IOleCache) and IOleCacheControl. The data cache also implements three other interfaces: IDataObject, which lets the client send data to the cache and retrieve the same later; IPersistStorage, so that the client can have the cache save its data and load it again; and IViewObject2, which lets the client ask the cache to draw a stored graphic to the screen or printer. If you have any experience with compound documents, you'll recognize that the needs described here are also common needs for compound documents themselves. In fact, OLE's data cache and the IOleCache* interfaces were originally created for OLE Documents. But the cache is still a useful service in and of itself, just as a viewable object is useful by itself. A viewable data object gives us the experience of seeing natural wonders with our own eyes, and caching allows us to archive photographs that help us to remember those experiences—even if they are only those of the hind end of a grocery store.