元件不僅要有外觀(繪圖),更要能響應使用者輸入。本文講解繪圖機制、滑鼠/鍵盤虛擬函數,並深入剖析 Delphi 特有的 DYNAMIC 關鍵字在 BCB 中的對應寫法。
TCustomControl / TGraphicControl 均提供 virtual void __fastcall Paint()。Paint 中使用 Canvas 繪圖(TCanvas 封裝 HDC)。Paint,可用 csDesigning 狀態區分視覺效果。Invalidate()(排入訊息佇列)或 Repaint()(立即重繪)。| 事件 | 虛擬函數原型 | 適用基類 |
|---|---|---|
| 滑鼠按下 | DYNAMIC void __fastcall MouseDown(TShiftState Shift, int X, int Y) | TControl 及衍生類 |
| 滑鼠移動 | DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y) | TControl 及衍生類 |
| 滑鼠釋放 | DYNAMIC void __fastcall MouseUp(TShiftState Shift, int X, int Y) | TControl 及衍生類 |
| 點擊 | DYNAMIC void __fastcall Click() | TControl 及衍生類 |
| 鍵盤按下 | DYNAMIC void __fastcall KeyDown(Word &Key, TShiftState Shift) | 需繼承 TCustomControl(擁有焦點) |
| 鍵盤釋放 | DYNAMIC void __fastcall KeyUp(Word &Key, TShiftState Shift) | 需繼承 TCustomControl |
| 字元輸入 | DYNAMIC void __fastcall KeyPress(Char &Key) | 需繼承 TCustomControl |
**關鍵點**:所有函數在 Delphi 中宣告為 dynamic,非 virtual。
dynamic 以控制框架整體體積(數百個控件類)。C++ 無 dynamic 關鍵字,Borland 擴充提供 DYNAMIC 宏:
// 正確重寫 MouseMove
class TMyControl : public TCustomControl {
protected:
DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y);
};
// 實作
DYNAMIC void __fastcall TMyControl::MouseMove(TShiftState Shift, int X, int Y) {
// 自定義邏輯...
TCustomControl::MouseMove(Shift, X, Y); // 呼叫父類
}
**切勿**使用 virtual 重寫 dynamic 方法,否則 VMT/DMT 不匹配導致呼叫失敗或崩潰。
// MyShape.h
class TMyShape : public TGraphicControl {
protected:
DYNAMIC void __fastcall Paint();
DYNAMIC void __fastcall MouseMove(TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseDown(TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TShiftState Shift, int X, int Y);
private:
TColor FPenColor, FBrushColor;
bool FDragging;
TPoint FDragStart;
__published:
__property TColor PenColor = {read=FPenColor, write=FPenColor, default=clBlack};
__property TColor BrushColor = {read=FBrushColor, write=FBrushColor, default=clWhite};
};
// MyShape.cpp
DYNAMIC void __fastcall TMyShape::Paint() {
Canvas->Pen->Color = FPenColor;
Canvas->Brush->Color = FBrushColor;
Canvas->Rectangle(0, 0, Width, Height);
}
DYNAMIC void __fastcall TMyShape::MouseDown(TShiftState Shift, int X, int Y) {
if (Shift.Contains(ssLeft)) { FDragging = true; FDragStart = Point(X, Y); }
TGraphicControl::MouseDown(Shift, X, Y);
}
DYNAMIC void __fastcall TMyShape::MouseMove(TShiftState Shift, int X, int Y) {
if (FDragging) {
Left += X - FDragStart.X;
Top += Y - FDragStart.Y;
}
TGraphicControl::MouseMove(Shift, X, Y);
}
DYNAMIC void __fastcall TMyShape::MouseUp(TShiftState Shift, int X, int Y) {
FDragging = false;
TGraphicControl::MouseUp(Shift, X, Y);
}
編譯安裝後,即可在調色盤拖放使用,支援設計時期拖曳移動與屬性編輯。
Paint,使用 Canvas,善用 Invalidate/Repaint。DYNAMIC 虛擬函數,**必須加 DYNAMIC 宏**。TCustomControl 擁有焦點可處理鍵盤;TGraphicControl 僅滑鼠。dynamic 機制,避免用 virtual 錯誤重寫。下一篇將進入 VCL 內部訊息核心:CN_*/CM_* 訊息與自訂事件機制。