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]
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);
}
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);
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;
}