Visual C++ 6.0 / Visual Studio 提供的 MFC ActiveX 控制項精靈,能自動生成包含 COleControlModule、COleControl 派生類、資源檔、註冊腳本的完整專案骨架。以下以建立包含三種控制項的範例專案為例:
SamCtrl)。CSam_windowCtrl:一般視窗控制項(繼承 CWnd,有 HWND)。CSam_nowindowCtrl:無視窗控制項(輕量,無 HWND,繪圖由容器代管)。CSam_editwindowCtrl:編輯視窗控制項(繼承 CEdit 或自訂編輯行為)。進階選項可設定:是否支援授權、是否為不可見控制項、預設大小、是否支援非同步下載等。
精靈為每個控制項生成三個核心類別:
CSam_windowCtrl (繼承 COleControl):控制項主體,處理繪圖、訊息、屬性/方法/事件分派。CSam_windowCtrlPropPage (繼承 COlePropertyPage):屬性頁,提供設計時的視覺化屬性編輯介面。CSam_windowCtrlFactory (繼承 COleObjectFactory):類別工廠,負責建立控制項實例、註冊/反註冊。SetMaterial透過 ClassWizard → Automation 頁籤 → Add Method:
SetMaterialLPCTSTR pszName, LPCTSTR pszBirth, LPCTSTR pszClass (皆為 [in])voidWizard 會自動:
.odl (Object Description Language) 檔中加入 dispinterface 定義。CSam_windowCtrl 類別宣告 void SetMaterial(LPCTSTR, LPCTSTR, LPCTSTR);。.cpp 生成 DISP_FUNCTION 對應巨集與空白實作函式。// Sam_windowCtrl.cpp
void CSam_windowCtrl::SetMaterial(LPCTSTR pszName, LPCTSTR pszBirth, LPCTSTR pszClass)
{
// 1. 參數檢查
if (!pszName || !pszBirth || !pszClass) return;
// 2. 更新成員變數 (需在 .h 中宣告 CString m_szName, m_szBirth, m_szClass;)
m_szName = pszName;
m_szBirth = pszBirth;
m_szClass = pszClass;
// 3. 觸發重繪
InvalidateControl(); // 無視窗控制項用這個;有視窗控制項可用 Invalidate()
}
TextColor (OLE_COLOR)ClassWizard → Automation → Add Property:
TextColorOLE_COLOR (32 位元色彩值,含調色盤索引)GetTextColor() / SetTextColor())// .h
OLE_COLOR m_clrTextColor; // 內部儲存 OLE_COLOR
COLORREF m_clrTextColorRGB; // 轉換後的 RGB 值,供繪圖使用
// .cpp
OLE_COLOR CSam_windowCtrl::GetTextColor()
{
return m_clrTextColor;
}
void CSam_windowCtrl::SetTextColor(OLE_COLOR clr)
{
// 1. 更新內部值
m_clrTextColor = clr;
// 2. 將 OLE_COLOR 轉為 COLORREF (需容器提供調色盤)
// 通常在 OnDraw 或需要繪圖時轉換,這裡僅標記需重繪
// 實際轉換:TranslateColor(clr, hPal, &m_clrTextColorRGB);
InvalidateControl();
// 3. 通知容器屬性已變更 (觸發 PropertyChanged 事件)
SetModifiedFlag();
BoundPropertyChanged(dispidTextColor);
}
OnDraw 核心邏輯所有視覺輸出集中在 OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid):
void CSam_windowCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// 1. 準備色彩 (若為 OLE_COLOR 需轉換)
COLORREF crText = m_clrTextColorRGB; // 假設已在 SetTextColor 或 OnDraw 前轉換
COLORREF crOld = pdc->SetTextColor(crText);
int nOldMode = pdc->SetBkMode(TRANSPARENT);
// 2. 繪製學生資料
CString strInfo;
strInfo.Format(_T("姓名: %s\n出生: %s\n班級: %s"), m_szName, m_szBirth, m_szClass);
pdc->DrawText(strInfo, rcBounds, DT_LEFT | DT_TOP | DT_WORDBREAK);
// 3. 還原 DC 狀態
pdc->SetTextColor(crOld);
pdc->SetBkMode(nOldMode);
}
CSam_nowindowCtrl) 特殊處理HWND,不接收 Windows 訊息 (WM_PAINT, WM_MOUSEMOVE 等)。OnDraw 中呼叫控制項繪圖,並轉發滑鼠/鍵盤事件透過 IOleInPlaceObjectWindowless 介面。SetCapture、SetFocus、工具提示等視窗功能。SamCtrl.ocx。.ocx 複製至目標機,以管理員權限執行 regsvr32 SamCtrl.ocx 註冊。<OBJECT classid="clsid:YOUR_CLSID" codebase="SamCtrl.cab#version=1,0,0,0"></OBJECT> (需打包 .cab 並簽章)。| 現象 | 可能原因 | 解決方向 |
|---|---|---|
| 屬性頁不顯示 | 未在 DoDataExchange 綁定控制項 | 檢查 DDX_Control/DDX_Text 等巨集 |
| 事件容器收不到 | 未實作 FireEvent 或 EventMap 缺少 EVENT_CUSTOM | 用 ClassWizard 加入事件,自動生成 FireClick 等函式 |
| 色彩顯示異常 | OLE_COLOR 未正確轉換 | 使用 TranslateColor 配合容器調色盤 |
| 授權失效 | 未正確實作 VerifyUserLicense/GetLicenseKey | 檢查 .lic 檔案內容與 _szLicString 是否一致 |
MFC 封裝了 ActiveX 控制項繁複的 COM 介面實作,透過 Wizard 與 ClassWizard,開發者可專注於商業邏輯:定義屬性/方法/事件 → 實作資料儲存與繪圖 → 處理視窗/無視窗差異。掌握 COleControl 虛函數 (OnDraw, OnEventAdvise, DoPropExchange 等) 的呼叫時機,是進階開發與除錯的關鍵。