許多讀者詢問元件開發的入門資源。誠實說,網路上系統性教材極少。最有效的途徑只有兩條:研讀 VCL 原始碼與參與 Borland 新聞群討論(forums.borland.com)。本系列末尾將贈送兩篇實戰文章:專業級元件原始碼解析、完整元件開發流程實作。
傳統 Windows 訊息(WM_*)不足以應對設計時期屬性變更(如修改 Font 後需重繪)。VCL 內部定義了兩類特殊訊息:
WM_COMMAND、WM_NOTIFY 等父視窗轉發的子控制項通知。父視窗收到後加上 CN_BASE 再分發給真正的子控制項處理。CM_FONTCHANGED(字體變更)、CM_ENABLEDCHANGED 等,定義於 Controls.pas。procedure TBitBtn.CNDrawItem(var Message: TWMDrawItem);
var
R: TRect;
begin
FCanvas.Handle := Message.DrawItemStruct.hDC;
R := ClientRect;
// ... 繪製圖文、按下偏移、焦點框 ...
if IsDown then OffsetRect(R, 1, 1);
TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout, FMargin,
FSpacing, State, False, DrawTextBiDiModeFlags(0));
if IsFocused and IsDefault then begin
R := ClientRect; InflateRect(R, -4, -4);
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Brush.Color := clBtnFace;
DrawFocusRect(FCanvas.Handle, R);
end;
FCanvas.Handle := 0;
end;
本質上仍是在 HDC 上繪圖,與 WM_PAINT 邏輯相似。
CN/CM 訊息無對應虛擬函數,需用訊息映射宏:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(CN_DRAWITEM, TWMDrawItem, CNDrawItem)
END_MESSAGE_MAP(TCustomControl)
void __fastcall CNDrawItem(TWMDrawItem &Msg);
上一篇出現的事件型別宣告:
typedef void __fastcall (__closure *THoverShapeEvent)(TObject* Sender, int Index);
typedef void __fastcall (__closure *TShapeSelectedEvent)(TObject* Sender, int Index);
THoverShapeEvent:函數指標型別名稱。void __fastcall:回傳型別與呼叫慣例。(__closure * ...):關鍵字,標記這是「閉包」指標,包含物件實例指標與成員函數位址。(TObject* Sender, int Index):參數列表。傳統 C++ 成員函數指標語法繁瑣且受限於特定類別:
// 傳統寫法:綁定特定類別 base
typedef void __fastcall (base::* BDCallBack)(String path, int type);
BDCallBack m = &base::func; // 只能指向 base::func
若多個類別都要當回呼,需為每類別宣告不同指標型別——極不靈活。
// 統一型別,任何類別的相容成員函數皆可指派
typedef void __fastcall (__closure *BDCallBack)(String path, int type);
class A {
BDCallBack func;
void DoSomething() { /* ... */ func("Found it", 0); }
};
class B {
void FuncB() {
A a;
a.func = this->callback; // 直接指派 B::callback
}
void __fastcall callback(String path, int type) { /* ... */ }
};
__closure 讓事件系統能「綁定任意物件的任意相容成員函數」,這正是 VCL 事件模型的基石。
The keyword
__closurewas added to support the VCL and is used when declaring event handler functions.
雖然官方說明極簡略,但實作上它解決了「成員函數指標跨類別通用」的核心難題。
下一篇將實作完整元件,示範自訂事件觸發、屬性編輯器整合、設計時/執行時行為差異處理等完整流程。