Stingray® Foundation : Chapter 5 Properties Package : ActiveX Controls
ActiveX Controls
ActiveX controls provide their own set of interfaces for accessing properties. Using those interfaces to access the properties of an ActiveX control from C++ code is a daunting task, unless you’re a seasoned COM developer with knowledge of the IDispatch and ITypeInfo interfaces. The Properties package simplifies access to ActiveX control properties by providing an implementation of the IProperty and IPropertyContainer interfaces for ActiveX controls. In addition to simplifying access to ActiveX control properties, it provides a uniform interface for accessing both C++ object properties and ActiveX control properties.
ActiveX Property Containers
The CAxPropertyContainer class extends a property container with support for ActiveX controls. It is a template class that takes a base class as a parameter, where the base class is any concrete implementation of the IPropertyContainer interface. The CAxPropertyContainer class wraps an existing property container class— CPropertyContainer by default— and extends it to support ActiveX controls.
Each ActiveX property container is associated with a single ActiveX control. The CAxPropertyContainer class accesses the ActiveX control through a pure virtual function called GetAxControl(). In other words, CAxPropertyContainer is an abstract base class. Derived classes must implement the GetAxControl() function and return the IUnknown pointer to the ActiveX control managed by the container. The CAxPropertyContainer class contains a member function called RegisterAxProperties(), which retrieves the properties from the control return by GetAxControl() and registers them in the property map. Example 28 shows an ActiveX property container based on CAxPropertyContainer.
Example 28 – ActiveX property container
class CCalendarControl : public CAxPropertyContainer
{
public:
CCalendarControl() : m_hWnd(NULL) {}
 
bool Create(HWND hParent)
{
bool bSuccess = false;
HRESULT hr;
AtlAxWinInit();
 
// Create MS calendar control
DWORD dwStyle = WS_CHILD;
CRect rcBounds(10,10,400,300);
m_hWnd = ::CreateWindow(_T("AtlAxWin"), NULL , dwStyle,
rcBounds.left, rcBounds.top,
rcBounds.Width(), rcBounds.Height(),
hParent, NULL,
_Module.GetModuleInstance(),
NULL);
 
if (m_hWnd)
{
IUnknown* pUnkHost;
hr = AtlAxGetHost(m_hWnd, &pUnkHost);
_ASSERTE(SUCCEEDED(hr));
CComQIPtr<IAxWinHostWindow> spAxWin(pUnkHost);
_ASSERTE(spAxWin);
 
hr = spAxWin->CreateControl(OLESTR("MSCAL.Calendar.7"),
m_hWnd, NULL);
 
if (SUCCEEDED(hr))
{
// Register the properties of the ActiveX control
// in the property map
RegisterAxProperties();
 
::ShowWindow(m_hWnd, SW_SHOW);
 
bSuccess = true;
}
 
pUnkHost->Release();
}
 
return bSuccess;
}
 
IUnknown* GetAxControl()
{
IUnknown* pUnk = 0;
if (m_hWnd)
pUnk = (IUnknown*)::SendMessage(m_hWnd,WM_ATLGETCONTROL,0,0);
return pUnk;
}
 
protected:
HWND m_hWnd;
};
Notice that the Create() function calls RegisterAxProperties(), which retrieves the properties from the ActiveX control and registers them by calling RegisterProperty() in the base class. The base class must implement a RegisterProperty() function with the following signature.
 
bool RegisterProperty(IProperty* pProp);
The CPropertyContainer class implements RegisterProperty(), so you don’t need to implement it yourself unless you use a base class other than CPropertyContainer. To create an ActiveX property container class, all you need to do is derive your class from CAxPropertyContainer, implement the GetAxControl() method, and call RegisterAxProperties(). Most of the code in Example 28 is devoted to creating the ActiveX control.
Using ActiveX Property Containers
Using an ActiveX property container is exactly like using any other property container, because SFL hides the implementation details behind a uniform interface. The ShowProperties() sample function shown earlier in Example 23 works against an ActiveX property container.
The function in Example 29 shows an example of setting the month and day properties of the calendar control. Notice that GetPropertyByName() is used to retrieve the property identifier before calling PutPropertyValue().
Example 29 – Accessing the properties of an ActiveX control
void InitCalendar(CCalendarControl& cal, int nMonth, int nDay)
{
VARIANT val;
val.vt = VT_I4;
 
IProperty* pMonth = cal.GetPropertyByName(OLESTR("Month"));
if (pMonth != NULL)
{
val.intVal = nMonth;
cal.PutPropertyValue(pMonth->GetId(), val);
}
IProperty* pDay = cal.GetPropertyByName(OLESTR("Day"));
if (pDay != NULL)
{
val.intVal = nDay;
cal.PutPropertyValue(pDay->GetId(), val);
}
}
 
The function below passes a calendar control to the ShowProperties() sample function shown in Example 30.
Example 30 – Passing an ActiveX property container to ShowProperties()
void ShowCalendarProperties(CCalendarControl& cal, CDC* pDC)
{
ShowProperties(&m_calendar, pDC);
}