本文以一個「撲克牌控件」為例,詳細演示如何使用 MFC ActiveX Control Wizard 一步步建立一個功能完整、可拖曳、支援屬性頁的 ActiveX 控件。適合初學 COM/ActiveX 開發的工程師參考。
Cards,其餘採預設值一路完成。IDB_CARDS 位圖為控件在工具箱中的圖示,可自行替換。透過 ClassWizard (檢視 → ClassWizard → Automation) 為控件類別 CCardsCtrl 新增以下屬性:
| 屬性名 | 類型 | 用途 |
|---|---|---|
| Value | short | 牌面點數 (1-54) |
| BackBmp | short | 背景圖案編號 |
| Background | BOOL | 顯示正面/背面 |
| IsMove | BOOL | 是否允許拖曳 |
| IsGoHome | BOOL | 拖曳後是否歸位 |
在建構函式 CCardsCtrl::CCardsCtrl() 中設定預設值:
value = 1; backbmp = 1; background = FALSE;
IsMove = FALSE; m_IsMove = FALSE; IsGoHome = TRUE;
覆寫 OnDraw,根據屬性決定繪製正面或背面:
void CCardsCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
CBitmap bitmap, *pOldBitmap;
CRect rect; GetClientRect(rect);
CDC memDC; CClientDC dc(this);
memDC.CreateCompatibleDC(&dc);
if (background) // 顯示背面
{
if (backbmp == 1) bitmap.LoadBitmap(255);
else if (backbmp == 2) bitmap.LoadBitmap(256);
}
else // 顯示正面
{
// 資源 ID 必須連續:IDB_BITMAP1 ~ IDB_BITMAP54
bitmap.LoadBitmap(IDB_BITMAP1 + value - 1);
}
pOldBitmap = memDC.SelectObject(&bitmap);
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOldBitmap);
}
關鍵提醒:資源檔 Resource.h 中牌面位圖 ID 必須連續排列,否則 IDB_BITMAP1 + value - 1 會載入錯誤圖片。
所有 SetXxx 函式結尾需呼叫 InvalidateControl() 觸發重繪:
void CCardsCtrl::SetValue(short nNewValue)
{
value = nNewValue;
SetModifiedFlag();
InvalidateControl(); // 立即更新畫面
}
處理 WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP 三個訊息:
OnLButtonDown:記錄起始座標、捕捉滑鼠、計算相對位移。OnMouseMove:若 m_IsMove 為真,依滑鼠位置呼叫 MoveWindow 移動控件。OnLButtonUp:釋放滑鼠捕捉,若 IsGoHome 為真則移回原位。Optional property name (如 background, value 等)。DoPropExchange 中序列化屬性:PX_Short(pPX, _T("value"), value, 1);
PX_Short(pPX, _T("backbmp"), backbmp, 1);
PX_Bool(pPX, _T("background"), background, FALSE);
PX_Bool(pPX, _T("IsMove"), IsMove, FALSE);
PX_Bool(pPX, _T("IsGoHome"), IsGoHome, TRUE);
Test。Cards Control 插入。SetValue() 動態變更牌面。至此,一個具備自訂繪圖、屬性綁定、拖曳互動、屬性頁設定的完整 ActiveX 控件已開發完成。