Integrating with Visual C

[Previous] [Next]

Another home in which ActiveX controls find themselves is a Visual C++ dialog resource. Now that we've seen how ActiveX controls and Visual Basic work together, let's see how to use ActiveX controls in Visual C++.

Using Component Gallery

The easiest way to add a control to your project is to use Component Gallery. Select Project_Add To Project_Components And Controls from the Visual C++ menu. This opens the Components And Controls Gallery dialog box, which asks if you want to insert standard Visual C++ components or registered ActiveX controls. When you select registered ActiveX controls, Visual C++ searches the Registry for all relevant controls, just as Visual Basic does. When you select a control from the list, Visual C++ uses the control's type library to build a C++ wrapper class around the control. The wrapper class handles all the details of activating the control, including CoCreateInstance, and of exchanging all the control and container interfaces. Visual C++ also takes the bitmap representing the control and places it in the resource editor control palette along with the other basic controls. (This, too, is similar to how Visual Basic works.)

Just as Visual Basic allows you to drag a control from the control palette to the form, Visual C++ lets you take a control from the palette and drag it onto a dialog box in your application. When you place the control on a dialog box, the control renders itself using IViewObject::Draw so that your dialog template contains a snapshot of how the control normally looks.

You can then use ClassWizard to add a data member to a dialog box that represents the control. Visual C++ makes this easy—it's just like connecting a member variable to a normal Windows control.

Visual C++ and Methods

When Visual C++ reads the control's type library, Visual C++ retrieves all the information needed to write a wrapper class around your control. The following code shows the wrapper that Component Gallery produces for the ATLMsgTraffic control:

 class CATLMsgTrafficCtl : public CWnd { protected:     DECLARE_DYNCREATE(CATLMsgTrafficCtl) public:     CLSID const& GetClsid()     {         static CLSID const clsid             = { 0xc30cca3e, 0xa482, 0x11d2, { 0x80, 0x38,                  0x64, 0x25, 0xd1, 0x0, 0x0, 0x0 } };         return clsid;     }     virtual BOOL Create(LPCTSTR lpszClassName,         LPCTSTR lpszWindowName, DWORD dwStyle,         const RECT& rect,         CWnd* pParentWnd, UINT nID,         CCreateContext* pContext = NULL)     { return CreateControl(GetClsid(),          lpszWindowName, dwStyle, rect,          pParentWnd, nID); }     BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,         const RECT& rect, CWnd* pParentWnd, UINT nID,         CFile* pPersist = NULL, BOOL bStorage = FALSE,         BSTR bstrLicKey = NULL)     { return CreateControl(GetClsid(),          lpszWindowName,          dwStyle, rect,          pParentWnd, nID,         pPersist, bStorage, bstrLicKey); } // Attributes public: // Operations public:     void SetBackColor(unsigned long newValue);     unsigned long GetBackColor();     void StartGraph();     void StopGraph();     long GetInterval();     void SetInterval(long nNewValue);     unsigned long GetGraphLineColor();     void SetGraphLineColor(unsigned long newValue);     long GetThreshold();     void SetThreshold(long nNewValue);     void Advise(LPUNKNOWN pATLMsgTrafficCtlEventsCustom,          long* pCookie);     void Unadvise(long lCookie); }; 

You should keep the following points about this class in mind:

  • It's based on CWnd; in other words, it's an MFC class. In fact, Component Gallery supports only adding controls to a project that uses the ClassWizard.
  • A few creation functions will wrap CoCreateInstance for you.
  • Each of the control's methods and properties are wrapped by normal C++-style functions.

In this example, the ATLMsgTraffic control lives inside a dialog box. The control is part of the dialog resource shown in the preceding code. When the dialog box appears, MFC creates all the controls listed in the dialog template and calls CoCreateInstance on the GUID listed in the resource:

 IDD_ABOUTBOX DIALOG DISCARDABLE  0, 0, 255, 197 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About useactivexcontrols" FONT 8, "MS Sans Serif" BEGIN     ICON            IDR_MAINFRAME,IDC_STATIC,11,17,21,20     LTEXT           "useactivexcontrols Version 1.0",                     IDC_STATIC,40,10,119,8,                     SS_NOPREFIX     LTEXT           "Copyright (C) 1999",IDC_STATIC,40,25,119,8     DEFPUSHBUTTON   "OK",IDOK,198,7,50,14,WS_GROUP     CONTROL         "",IDC_ATLMSGTRAFFICCTL1,                     "{C30CCA3E-A482-11D2-8038-6425D1000000}",                     WS_TABSTOP,7,55,                     214,135 END 

Visual C++ then sizes and positions the control using the coordinates listed in the dialog template.

Once the control is loaded and in place, you can start calling methods on the object. For example, if you want the graph to start as soon as the dialog box appears, you might add a call to the control's StartGraph function as part of the WM_INITDIALOG processing, as shown in the following code:

 BOOL CAboutDlg::OnInitDialog()  {     CDialog::OnInitDialog();     m_atlctl.StartGraph();     return TRUE;   } 

Visual C++ and Properties

Visual C++ manages a control's properties a bit differently than Visual Basic does. Of course, you can manipulate the control's properties through the C++ wrapper class. (Notice the functions GetInterval and SetInterval in the C++ wrapper class.) You can also configure the control's properties at design time. When working with the control in a dialog box, highlight the control and activate its property pages by right-clicking and selecting Properties from the context menu or by pressing Alt-Enter. Microsoft Visual Studio retrieves the control's property pages (via the ISpecifyPropertyPages interface implemented by the control). Visual Studio calls CoCreateInstance on each GUID listed by the control and places each property page inside a dialog box frame, as shown in Figure 13-4.

click to view at full size.

Figure 13-4. The control's property pages in Visual Studio.

As you change the control's properties (such as graph line color or background color), Visual Studio saves these properties in the dialog template. If you look at the resource script, you can see where the properties are stored. The following code from the dialog resource shows how the properties are stored:

 // From the resource script IDD_ABOUTBOX DLGINIT BEGIN     IDC_ATLMSGTRAFFICCTL1, 0x376, 40, 0,         0x0000, 0x0000, 0x0300, 0x0000, 0x0013, 0x0000, 0x8000,         0x0013, 0x0017, 0x8000, 0x0003, 0x0064, 0x0000, 0x0003,         0x01c9, 0x0000, 0x212d, 0x0000, 0x16a2, 0x0000, 0 END 

Visual Studio relies on the control implementing IPersistStreamInit to handle property persistence. To see this, put a breakpoint on the property map. You'll see that Microsoft Developer Studio uses IPersistStreamInit every time you modify the control's properties through the property pages. Unlike Visual Basic, which falls back on IPersistPropertyBag if IPersistStream is unavailable, Visual Studio can't create your object if your control doesn't expose IPersistStreamInit.

MFC and Ambient Properties

Now let's examine how MFC handles ambient properties—those properties managed by the container but visible to the control. For example, if you want to make the background of your control match the background of the host application, you'd ask the host application what its background is and paint the control with that background.

When a control is running within an MFC-based application, MFC holds on to several ambient properties. As an ActiveX control host, MFC supports autoclipping, message reflection, and mnemonics, so MFC returns TRUE for the following ambient property dispatch IDs (DISPIDs):

  • DISPID_AMBIENT_AUTOCLIP
  • DISPID_AMBIENT_MESSAGEREFLECT
  • DISPID_AMBIENT_SUPPORTSMNEMONICS
  • DISPID_AMBIENT_USERMODE

MFC returns FALSE for these two ambient property DISPIDs:

  • DISPID_AMBIENT_SHOWGRABHANDLES
  • DISPID_AMBIENT_UIDEAD

MFC advertises itself as having a three-dimensional appearance (DISPID_AMBIENT_APPEARANCE). The default ambient background color is the color of the window that is hosting the control, and the default ambient foreground color is the host window's text color.

Now let's take a look at how MFC handles control events.

MFC and Control Events

When we loaded the ATLMsgTraffic control, Component Gallery read the type library. In addition to building the C++ class wrapper, Component Gallery absorbed information about the control's event set. So when you add the control to a dialog box, you can add event handlers. MFC sets up the code so that when the control calls back to the dialog box, the control events can be handled the same way that window messages are handled—even though the events are function calls from the control to the client. Just fire up the ClassWizard and let it add the event handlers for you. Figure 13-5 illustrates this.

click to view at full size.

Figure 13-5. Using ClassWizard to add an ActiveX control event handler.



Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127

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