Click to return to the Component Development home page    
Web Workshop  |  Component Development

MFC Step-by-Step


Jay Massena and Douglas Hodges

Updated: February 12, 1997

Summary

This document is a step-by-step tutorial for creating the Bgsound design-time control using Microsoft® Foundation Classes (MFC).

Contents
Step 1: Use MFC OLE ControlWizard to create your control
Step 2: Add IActiveDesigner interface to class definition
Step 3: Implement IActiveDesigner interface]



Create an ActiveX Control

Step 1: Use MFC OLE ControlWizard to Create Your Control

A. Create new project workspace and run OleControlWizard to create a project named "BGSound". Set to Windowless activation in advanced options.

B. Add the Design-time Control SDK include directory to INCLUDE Path

c:\progra~1\dcsdk\Samples\Mfc\include

C. Use View.ClassWizard to add Property Get/Set methods

BSTR Source
short Loop

D. Add Member Variables (in BGSoundCtl.h)

   // control properties
   short m_nLoop;
   CString m_strSource;

E. 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();
}

F. 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);

G. 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);
}

TopBack to top

Add Design-time Control Support

Step 2: Add IActiveDesigner Interface to Class Definition

A. Add IActiveDesigner interface definition (in BGSoundCtl.h)

#include "designer.h"
#include "webdc.h"

B. 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);

TopBack to top

Step 3: Implement IActiveDesigner Interface

A. 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"

B. Add Interface Map (in BGSoundCtl.cpp)

////////////////////////////////////////////////////////////////////////
// Interface Map

BEGIN_INTERFACE_MAP(CBGSoundCtrl, COleControl)
   INTERFACE_PART(CBGSoundCtrl, IID_IActiveDesigner, ActiveDesigner)
END_INTERFACE_MAP()

C. 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;
}



Back to topBack to top

Did you find this material useful? Gripes? Compliments? Suggestions for other articles? Write us!

© 1999 Microsoft Corporation. All rights reserved. Terms of use.