The final two members of IViewObject2—SetAdvise and GetAdvise—work with IAdviseSink::OnViewChange independently of IAdviseSink::OnDataChange, which we saw in Chapter 10. SetAdvise establishes or ends a notification loop, and GetAdvise retrieves the last IAdviseSink pointer seen in SetAdvise.
IAdviseSink::OnViewChange informs a client that an object's view changed, not necessarily some underlying data. This is why a client calls IDataObject::DAdvise for data change notifications and IViewObject2::SetAdvise for view changes. As an example, consider a data object attached to a spreadsheet whose view is also that spreadsheet. A change in one of the cells would fire OnDataChange but would fire OnViewChange for DVASPECT_CONTENT only if that cell is visible in the rendering. A change to the spreadsheet does not, however, change a DVASPECT_ICON rendering, which is unrelated to the actual data underneath. A change to the actual spreadsheet filename, on the other hand, might change the icon aspect (if the filename is the icon's label) but not the content aspect, which shows only the spreadsheet cells. In addition, a change to the rotation of a chart might fire OnViewChange, although the actual data is the same. When data and view changes occur together, which notification is fired first is variable between objects. Clients should not depend on any particular order.
Clients call IViewObject2::SetAdvise with dwAspect set to whatever aspects it is displaying in an active fashion. A view object that doesn't support notifications at all returns NOTIFY_E_ADVISENOTSUPPORTED. Otherwise, the dwAdvf flags (the same ADVF_* flags as for IDataObject::DAdvise) describe the nature of the notifications desired; 0 means "normal notification," ADVF_PRIMEFIRST generates an OnViewChange call immediately, and ADVF_ONLYONCE will have the object release the advise sink after the first OnViewChange call. No other ADVF_* flags are relevant. Furthermore, pAdvSink points to the client's IAdviseSink implementation. A client will generally invalidate some region of its window inside IAdviseSink::OnViewChange and then call IViewObject2::Draw later during a repaint. A client calls SetAdvise with a NULL in pAdvSink to terminate the advisory connection.
Another point about SetAdvise is that each viewable object maintains only a single IAdviseSink pointer. A viewable object does not support multicasting. Any call to SetAdvise will release the formerly installed sink and install the new one. This will not cause a conflict for multiple clients because IViewObject2 can be implemented only on an in-process object and there will generally be only one client for any one instantiation of such an object. There is no conflict with multiple remote clients. The possibility exists, of course, that multiple components in a process will try to access the same instance of a viewable object, but that has not been shown to be a problem. Because components are usually isolated from one another, each would use its own instance of the viewable object. Only components with intimate knowledge of each other could be in conflict, in which case it is entirely their responsibility to determine how to handle notifications.
As mentioned earlier, a viewable object must be in-process, and as part of an in-process handler it works in conjunction with a local server. To keep the two synchronized, the handler object needs to know when the local object changes data. The handler object can then generate an IAdviseSink::OnViewChange call to the client if necessary. To do this, the handler object must implement a sink of its own and connect it to the local object through IDataObject::DAdvise. When this handler sink receives IAdviseSink::OnDataChange, it can check whether the change affects any current view for which the client wants a notification. If so, the handler object turns around and calls the client's IAdviseSink::OnViewChange, which it can do directly from within the handler object's own OnDataChange because the handler is in the client process already. The handler's sink is a separate object from the viewable object in that same handler, so they must have separate implementations of QueryInterface.