Step-by-Step: Building a Web Design-time Control with MFC

October 6, 1996

Contents

Create an ActiveX control
   Step 1: Use MFC OLE ControlWizard to create your control
Add Web design-time control support
   Step 2: Add IActiveDesigner interface to class definition
   Step 3: Implement IActiveDesigner interface

Summary

This document is a step-by-step tutorial for creating the BGSound Web design-time ActiveX control using MFC.

Create an ActiveX control

Step 1: Use MFC OLE ControlWizard to create your control

  1. Create new project workspace and run OleControlWizard, creating a project named "BGSound." Set to Windowless activation in advanced options
  2. Add Design-time Control SDK include directory to INCLUDE Path
    
    c:\progra~1\dcsdk\Samples\Mfc\include
    
  3. Use View.ClassWizard to add Property Get/Set methods
    
    BSTR Source
    short Loop
    
  4. Add Member Variables (in bgsoundctl.h)
    
    // control properties
    short m_nLoop;
    CString m_strSource;
    
  5. Add Get/Set Methods (in bgsoundctl.cpp)
    
    short CBGSoundCtrl::GetLoop() 
    {
          return m_nLoop;
    }
    
    void CBGSoundCtrl::SetLoop(short nNewValue) 
    {
          // verify loop property is valid
          if (nNewValue >= -1)
          {
                 // set our internal loop value
                 m_nLoop = nNewValue;
    
                 // notify container that a property was modified
                 SetModifiedFlag();
    
                 // notify property browsers that this property changed
                 BoundPropertyChanged(dispidLoop);
          }
          else
          {
                 ThrowError(CTL_E_INVALIDPROPERTYVALUE, 
                                _T("Loop must be an integer greater than or equal to -1"));
          }
    }
    
    BSTR CBGSoundCtrl::GetSource() 
    {
          return m_strSource.AllocSysString();
    }
    
    void CBGSoundCtrl::SetSource(LPCTSTR lpszNewValue) 
    {
        // set our internal source string
        m_strSource = lpszNewValue;
    
        // notify container that a property was modified
        SetModifiedFlag();
    
        // notify property browsers that this property changed
        BoundPropertyChanged(dispidSource);
    
        // make sure we get repainted since 'Source' is our caption
        InvalidateControl();
    }
    
  6. Add Support to persist properties (in CBGSoundCtrl::DoPropExchange)
    
    // persist our properties
    PX_String(pPX, _T("Source"), m_strSource, _T(""));
    PX_Short(pPX, _T("Loop"), m_nLoop, -1);
    
  7. Add Drawing Code (in CBGSoundCtrl::OnDraw)
    
    void CBGSoundCtrl::OnDraw(
                        CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
    {
        CFont fnt;
        CRect rcText;
    
        // get text rect;
        rcText = rcBounds;
        rcText.left += 2;
        rcText.right -= 2;
        rcText.top += 2;
        rcText.bottom -= 2;
    
        // create font
        fnt.CreatePointFont(160, _T("MS Sans Serif"), pdc);
        CFont* pOldFont = pdc->SelectObject(&fnt);
        pdc->SetBkMode(TRANSPARENT);
    
        // fill our rect with gray
        pdc->FillRect(rcBounds,                                                                               CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH)));
    
        // draw an edge
        pdc->DrawEdge(&(RECT)rcBounds, EDGE_RAISED, BF_RECT);
    
        // draw the Source text
        pdc->DrawText(_T("\"") + m_strSource + _T("\""), &(RECT)rcText, DT_WORDBREAK | DT_CENTER); 
    
        // return to old font
        pdc->SelectObject(pOldFont);
    }
    

Add Web design-time control support

Step 2: Add IActiveDesigner interface to class definition

  1. Add IActiveDesigner interface definition (in bgsoundctl.h)
    
    #include "designer.h"
    #include "webdc.h"
    
  2. Declare Interface_Map and IActiveDesigner interface (in bgsoundctl.h - protected)
    
    // Interface maps
    DECLARE_INTERFACE_MAP();
    
    // IActiveDesigner Interface
    BEGIN_INTERFACE_PART(ActiveDesigner, IActiveDesigner)
       STDMETHOD(GetRuntimeClassID)(CLSID *pclsid);
       STDMETHOD(GetRuntimeMiscStatusFlags)(DWORD *dwMiscFlags);
       STDMETHOD(QueryPersistenceInterface)(REFIID riidPersist);
       STDMETHOD(SaveRuntimeState)(REFIID riidPersist, REFIID riidObjStgMed, void *pObjStgMed);
       STDMETHOD(GetExtensibilityObject)(IDispatch **ppvObjOut);
    END_INTERFACE_PART(ActiveDesigner)
    
    virtual HRESULT GetRuntimeText(CString& strText);
    

Step 3: Implement IActiveDesigner interface

  1. Add intialization of IActiveDesigner GUIDs (in bgsoundctl.cpp after stdafx.h)
    
    // Define and initialize required GUIDs
    #include <initguid.h>
    #include "designer.h"
    #include "webdc.h"
    
  2. Add Interface Map (in bgsoundctl.cpp)
    
    ////////////////////////////////////////////////////////////////////////
    // Interface Map
    
    BEGIN_INTERFACE_MAP(CBGSoundCtrl, COleControl)
          INTERFACE_PART(CBGSoundCtrl, IID_IActiveDesigner, ActiveDesigner)
    END_INTERFACE_MAP()
    
    
  3. Add IActiveDesigner interface implementation (in bgsoundctl.cpp)
    
    ////////////////////////////////////////////////////////////////////////
    // CBGSoundCtrl::XActiveDesigner - IActiveDesigner interface
    //
    // The following functions represent the implementation of the IActiveDesigner
    // interface. For the most part, these functions can be left as is. The
    // SaveRuntimeState() function will call the CBGSoundCtrl::GetRuntimeText()
    // function when it needs to generate run-time text.
    
    STDMETHODIMP_(ULONG) CBGSoundCtrl::XActiveDesigner::AddRef(void)
    {
          METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
          return (ULONG)pThis->ExternalAddRef();
    }
    
    STDMETHODIMP_(ULONG) CBGSoundCtrl::XActiveDesigner::Release(void)
    {
          METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
          return (ULONG)pThis->ExternalRelease();
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::QueryInterface(REFIID iid,
                                                                                              LPVOID far* ppvObj)
    {
          METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
          return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetRuntimeClassID(CLSID *pclsid)
    {
        *pclsid = CLSID_NULL;
        return S_FALSE;
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetRuntimeMiscStatusFlags(DWORD *pdwMiscFlags)
    {
          if (!pdwMiscFlags)
                 return E_INVALIDARG;
          *pdwMiscFlags = NULL;
          return E_UNEXPECTED;
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::QueryPersistenceInterface(REFIID riid)
    {
          if (riid == IID_IPersistTextStream)
                 return S_OK;
          return S_FALSE;
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::SaveRuntimeState
    (
          REFIID riidPersist,
          REFIID riidObjStgMed,
          void  *pObjStgMed
    )
    {
          HRESULT  hr;
          CString  cstrText;
          BSTR     bstrText;
    
          METHOD_MANAGE_STATE(CBGSoundCtrl, ActiveDesigner)
    
          if (riidPersist != IID_IPersistTextStream)
                 return E_NOINTERFACE;
    
          if (riidObjStgMed != IID_IStream)
                 return E_NOINTERFACE;
    
          hr = pThis->GetRuntimeText(cstrText);
          if (SUCCEEDED(hr))
          {
                 bstrText = cstrText.AllocSysString();
                 if (bstrText)
                 {
                        hr = ((IStream *)pObjStgMed)->Write(bstrText,                                     SysStringByteLen(bstrText) + sizeof(OLECHAR), NULL);
                     SysFreeString(bstrText);
                 }
                 else
                     hr = E_OUTOFMEMORY;
          }
    
          return hr;
    }
    
    STDMETHODIMP CBGSoundCtrl::XActiveDesigner::GetExtensibilityObject(IDispatch **ppvObjOut)
    {
          if (!ppvObjOut)
                 return E_INVALIDARG;
          return this->QueryInterface(IID_IDispatch, (void **)ppvObjOut);
    }
    
    ////////////////////////////////////////////////////////////////////////
    // CBGSoundCtrl::GetRuntimeText - Generates run-time text for the control
    //
    // GetRuntimeText() is called whenever the container needs to update the
    // run-time text after property changes or other state changes are made.
    
    HRESULT CBGSoundCtrl::GetRuntimeText(CString& strText)
    {
        // our run-time Text template
        CString strTemplate = _T("<BGSOUND SRC=\"%s\" LOOP=%d>");
    
        // format our string with our properties
          strText.Format(strTemplate, m_strSource, m_nLoop);
    
          return S_OK;
    }
    
    
    
    © 1997 Microsoft Corporation. All rights reserved. Legal Notices.