Framework Support: CComControl and IPropertyNotifySinkCP

 < Free Open Study > 



Framework Support: CComControl<> and IPropertyNotifySinkCP<>

ATL controls inherit a majority of their functionality from the CComControl<> template and its base classes, CComControlBase<> and CWindowImpl<>:

// CComControl is a core base class to any full control. template <class T, class WinBase = CWindowImpl< T > > class ATL_NO_VTABLE CComControl : public CComControlBase, public WinBase { public: ... };

The functionality provided by CComControl<> itself is not too elaborate. Beyond providing a default message map which is chained together in your derived class (as we saw earlier in the chapter), CComControl<> provides implementations for the methods of the COM interface IPropertyNotifySink. The definition of this interface is found in <ocidl.idl>:

// If a control implements this interface, it is able to inform the container // when a given property has changed, or is about to change. [ object, uuid(9BFBBC02-EFF1-101A-84ED-00AA00341D07)] interface IPropertyNotifySink : IUnknown {      // Item DISPID has changed.      HRESULT OnChanged( [in] DISPID dispID );      // Item DISPID is about to change, OK with you?      HRESULT OnRequestEdit( [in] DISPID dispID); }; 

This interface is used to establish another type of connection between the control and client beyond a typical IConnectionPointContainer interaction used to handle your custom events. IPropertyNotifySink is used to inform a client when a given property in the [default] interface has changed or is about to change, giving the client a chance to react. Like a standard connectable object, the host container will create a sink to receive events from the IPropertyNotifySink interface, and your class will call the methods of the IPropertyNotifySink proxy to notify the container when necessary. CComControl<> provides an implementation of each method for you using FireOnChanged() and FireOn- RequestEdit().

If you examine your inheritance chain for CShapesControl, you will see that one of your base classes is named IPropertyNotifySinkCP<>. This ATL template is used to bring in the signatures for the two methods of IPropertyNotifySink, via a typedef to another ATL class, _ATL_PROP_NOTIFY_EVENT_CLASS. The levels of indirection are common in ATL source code, and while it may seem like a bit of a maze, it does allow you to extend the framework where you see fit. Here are the relevant players used to support the IPropertyNotifySink interface:

// IPropertyNotifySinkCP is one of your base classes, which indirectly brings // in support for IPropertyNotifySink. template <class T, class CDV = CComDynamicUnkArray > class ATL_NO_VTABLE IPropertyNotifySinkCP :      public IConnectionPointImpl<T, &IID_IPropertyNotifySink, CDV> { public:      typedef CFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS; }; // CFakeFirePropNotifyEvent sets the stub code for the methods of IPropertyNotifySink, // but CComControl<> implements them. class CFakeFirePropNotifyEvent { public:      static HRESULT FireOnRequestEdit(IUnknown* /*pUnk*/, DISPID /*dispID*/)      {           return S_OK;      }      static HRESULT FireOnChanged(IUnknown* /*pUnk*/, DISPID /*dispID*/)      {           return S_OK;      } }; typedef CFakeFirePropNotifyEvent _ATL_PROP_NOTIFY_EVENT_CLASS; 

Supporting [bindable] and [requestedit] Properties

So now that you have support for IPropertyNotifySink, when might we indeed call FireOnRequestEdit() and FireOnChanged()? First off, you should not call these methods unless the properties you are providing notifications for have been marked with the IDL [requestedit] and [bindable] attributes. Those properties marked with [requestedit] should call FireOnRequestEdit() within any [propput] implementation before changing the value. This will in effect say to the container, "Hey, I want to change this property, got a problem with this?" The container might very well say, "Yes I have a problem with that; don't change the value just yet or you will mess things up on my end" and return a failed HRESULT to you. If you wish to give your container the chance to vote on your property assignments, first add the [requestedit] attribute to the correct property in your IDL file. Next, in your [propput] implementation code, call FireOnRequestEdit() and test the returned HRESULT.

As for FireOnChanged(), this is a method that should only be called for a property marked with the [bindable] attribute. If you have properties marked with the [bindable] attribute, your client expects to be informed when a change has occurred, and thus you should call FireOnChanged() after you have assigned a new value within a [propput] implementation. Here are the relevant changes we would need to make in ShapesControl's ShapeType property to tell when a property is about to change and when it has changed:

// Making ShapeType bindable and giving a container the chance to say 'NO'. interface IShapesControl : IDispatch { ...      [propget, id(1), helpstring("property ShapeType"), bindable, requestedit]      HRESULT ShapeType([out, retval] CURRENTSHAPE *pVal);      [propput, id(1), helpstring("property ShapeType"), bindable, requestedit]      HRESULT ShapeType([in] CURRENTSHAPE newVal); }; STDMETHODIMP CShapesControl::put_ShapeType(CURRENTSHAPE newVal) {      // Is this change OK with the container?      if(FAILED(FireOnRequestEdit(1)))          // [id(1)] = ShapeType           return S_FALSE;      // Check bounds.      if(newVal >= shapeCIRCLE && newVal <= shapeRECTANGLE)           m_shapeType = newVal;      // Redraw the view (i.e., call OnDraw())      FireViewChange();      // Tell container we have changed the value.      FireOnChanged(1);                         // [id(1)] = ShapeType      return S_OK; }



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net