Step 6: Adding a Property Page

Property pages are implemented as separate COM objects, which allow property pages to be shared if required. To add a property page to your control you can use the ATL Object Wizard.

Start the ATL Object Wizard and select Controls as the category on the left. Select Property Page on the right, then click Next.

You again get the dialog box allowing you to enter the name of the new object. Call the object PolyProp and enter that name in the Short Name edit box.

Notice that the Interface edit box is grayed out. This is because a property page doesn't need a custom interface.

Click on the Strings tab to set the title of the property page. The title of the property page is the string that appears in the tab for that page. Type &Polygon as the title. The Doc String is a description that a property frame uses to put in a status line or tool tip. Note that the standard property frame currently doesn't use this string, but you can set it anyway. You're not going to generate a Helpfile at the moment, so erase the entry in that text box. Click OK and the property page object will be created.

The following three files are created:

File Description
PolyProp.h Contains the C++ class CPolyProp, which implements the property page.
PolyProp.cpp Includes the PolyProp.h file.
PolyProp.rgs The registry script that registers the property page object.

The following code changes are also made:

Now add the fields that you want to appear on the property page. Notice that in Polygon.rc the dialog is empty except for a label that tells you to insert your controls here. Delete that label and add one that contains the text "Sides:". Next to the label add an edit box and give it an ID of IDC_SIDES.

Include Polygon.h at the top of the PolyProp.h file:

#include "Polygon.h"  // definition of IPolyCtl

Now enable the CPolyProp class to set the number of sides in your object when the Apply button is pressed. Change the Apply function in PolyProp.h as follows.

STDMETHOD(Apply)(void)
{
   USES_CONVERSION;
   ATLTRACE(_T("CPolyProp::Apply\n"));
   for (UINT i = 0; i < m_nObjects; i++)
   {
      CComQIPtr<IPolyCtl, &IID_IPolyCtl> pPoly(m_ppUnk[i]);
      short nSides = (short)GetDlgItemInt(IDC_SIDES);
      if FAILED(pPoly->put_Sides(nSides))
      {
         CComPtr<IErrorInfo> pError;
         CComBSTR         strError;
         GetErrorInfo(0, &pError);
         pError->GetDescription(&strError);
         MessageBox(OLE2T(strError), _T("Error"), MB_ICONEXCLAMATION);
         return E_FAIL;
      }
   }
   m_bDirty = FALSE;
   return S_OK;
}

A property page could have more than one client attached to it at a time, so the Apply function loops around and calls put_Sides on each client with the value retrieved from the edit box. You are using the CComQIPtr class, which performs the QueryInterface on each object to obtain the IPolyCtl interface from the IUnknown (stored in the m_ppUnk array).

The code now checks that setting the Sides property actually worked. If it fails, the code displays a message box displaying error details from the IErrorInfo interface. Typically, a container asks an object for the ISupportErrorInfo interface and calls InterfaceSupportsErrorInfo first, to determine whether the object supports setting error information. But since it's your control, you can forego that check.

CComPtr helps you by automatically handling the reference counting, so you don't need to call Release on the interface. CComBSTR helps you with BSTR processing, so you don't have to perform the final SysFreeString call. You also use one of the various string conversion classes, so you can convert the BSTR if necessary (this is why we add the USES_CONVERSION macro at the start of the function).

You also must set the property page's dirty flag to indicate that the Apply button should be enabled. This occurs when the user changes the value in the Sides edit box. Right-click the property page class (CPolyProp) in ClassView and then select Add Windows Message Handler... from the shortcut menu.  Select IDC_SIDES from the object box and then add the EN_CHANGE message.

Now add the following code in Polyprop.h to the OnChangeSides function (deleting any code that the wizard put there):

LRESULT OnChangeSides(WORD wNotify, WORD wID, HWND hWnd, BOOL& bHandled)
{
   SetDirty(TRUE);
   return 0;
}

OnChangeSides will be called when a WM_COMMAND message is sent with the EN_CHANGE notification for the IDC_SIDES control. OnChangeSides then calls SetDirty and passes TRUE to indicate the property page is now dirty and the Apply button should be enabled.

Now, add the property page to your control. The ATL Object Wizard doesn't do this for you automatically, since there could be multiple controls in your project. Open PolyCtl.h and add this line to the property map:

   PROP_ENTRY("Sides", 1, CLSID_PolyProp)

The control's property map now looks like this:

BEGIN_PROP_MAP(CPolyCtl)
   PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
   PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
   PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage)
   PROP_ENTRY("Sides", 1, CLSID_PolyProp)
   // Example entries
   // PROP_ENTRY("Property Description", dispid, clsid)
   // PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()

You could have added a PROP_PAGE macro with the CLSID of your property page, but if you use the PROP_ENTRY macro as shown, the Sides property value is also saved when the control is saved. The three parameters to the macro are the property description, the DISPID of the property, and the CLSID of the property page that has the property on it. This is useful if, for example, you load the control into Visual Basic and set the number of Sides at design time. Since the number of Sides is saved, when you reload your Visual Basic project the number of Sides will be restored.

Now build that control and insert it into ActiveX Control Test Container. Then, in Test Container, on the Edit menu click PolyCtl Class Object. The property page appears; chose the Polygon tab.

The Apply button is initially disabled. Start typing a value in the Sides edit box and the Apply button will become enabled. After you have finished entering the value, click the Apply button. The control display changes and the Apply button is again disabled. Try entering an invalid value and you should see a message box containing the error description that you set from the put_Sides function.

Next you'll put your control on a Web page.

Back to Step 5On to Step 7