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