VCL 來源為 Delphi,其虛擬函數有兩種宣告:virtual(標準虛擬函數表)與 dynamic(動態虛擬函數表,節省記憶體,呼叫稍慢)。C++ 無 dynamic 關鍵字,BCB 提供 DYNAMIC 宏 實現相同語意。
// VCL 原始碼 (Delphi)
procedure KeyDown(var Key: Word; Shift: TShiftState); dynamic;
procedure KeyUp(var Key: Word; Shift: TShiftState); dynamic;
procedure KeyPress(var Key: Char); dynamic;
// C++ 衍生類別重寫
DYNAMIC void __fastcall MouseMove(Classes::TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall KeyDown(Word &Key, Classes::TShiftState Shift);
| 事件類別 | 虛擬函數 | 備註 |
|---|---|---|
| 滑鼠 | MouseDown | 按下 |
| MouseUp | 釋放 | |
| MouseMove | 移動 | |
| Click | 單擊(含雙擊邏輯) | |
| 鍵盤 | KeyDown | 按下(含虛擬鍵碼) |
| KeyUp | 釋放 | |
| KeyPress | 字元輸入(ANSI 字元) |
注意:鍵盤虛擬函數要求類別直接或間接繼承自 TCustomControl(擁有輸入焦點能力)。
繼承自 TCustomControl,支援直線繪製、選取、拖曳調整,展示事件處理與自訂事件觸發。
class PACKAGE TVecCanvas : public TCustomControl {
private:
TList* ShapeList;
CShape* CurrentShape;
bool Down;
TPoint LastPos, NewPos;
THoverShapeEvent FOnHoverShape;
ToolType FTool;
TShapeSelectedEvent FOnShapeSelected;
DYNAMIC void __fastcall MouseDown(TMouseButton Button, Classes::TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseUp(TMouseButton Button, Classes::TShiftState Shift, int X, int Y);
DYNAMIC void __fastcall MouseMove(Classes::TShiftState Shift, int X, int Y);
void __fastcall DoHoverShape(int index);
void __fastcall SetTool(ToolType value);
void __fastcall DoShapeSelected(int index);
protected:
void __fastcall Paint(); // 重繪核心
public:
__fastcall TVecCanvas(TComponent* Owner);
__fastcall ~TVecCanvas();
int __fastcall ShapeAtPos(int x, int y);
__published:
__property Color;
__property OnClick; // 繼承自祖先,僅暴露
__property THoverShapeEvent OnHoverShape = { read=FOnHoverShape, write=FOnHoverShape };
__property Align;
__property OnMouseMove;
__property OnMouseDown;
__property OnMouseUp;
__property ToolType Tool = { read=FTool, write=SetTool };
__property TShapeSelectedEvent OnShapeSelected = { read=FOnShapeSelected, write=FOnShapeSelected };
};
__fastcall TVecCanvas::TVecCanvas(TComponent* Owner)
: TCustomControl(Owner) {
ShapeList = new TList();
CurrentShape = NULL;
Down = false;
FTool = ttLine;
}
void __fastcall TVecCanvas::MouseDown(TMouseButton Button, Classes::TShiftState Shift, int X, int Y) {
if (Button != mbLeft) return;
switch (FTool) {
case ttLine:
LastPos = NewPos = Point(X, Y);
Down = true;
break;
case ttSelect:
if (int sh = ShapeAtPos(X, Y); sh != -1) DoShapeSelected(sh);
break;
}
}
void __fastcall TVecCanvas::MouseUp(TMouseButton Button, Classes::TShiftState Shift, int X, int Y) {
Down = false;
if (Button == mbLeft && FTool == ttLine) {
CShape* sh = new CLine();
sh->Start = LastPos;
sh->End = Point(X, Y);
sh->Type = stLine;
ShapeList->Add(sh);
Canvas->MoveTo(LastPos.x, LastPos.y);
Canvas->LineTo(X, Y);
}
}
void __fastcall TVecCanvas::MouseMove(Classes::TShiftState Shift, int X, int Y) {
if (!Down) {
if (int sh = ShapeAtPos(X, Y); sh != -1) DoHoverShape(sh);
return;
}
if (FTool == ttLine) {
// XOR 模式繪製橡皮筋效果
Canvas->Pen->Mode = pmNotXor;
Canvas->MoveTo(LastPos.x, LastPos.y);
Canvas->LineTo(NewPos.x, NewPos.y);
NewPos = Point(X, Y);
Canvas->MoveTo(LastPos.x, LastPos.y);
Canvas->LineTo(NewPos.x, NewPos.y);
Canvas->Pen->Mode = pmCopy;
}
}
void __fastcall TVecCanvas::Paint() {
for (int n = 0; n < ShapeList->Count; n++) {
CShape* sh = (CShape*)ShapeList->Items[n];
sh->Draw(Canvas);
}
}
__property OnClick 僅將祖先已實作的事件發布至物件檢視器,無需重寫邏輯。TButton;若需精簡屬性集繼承 TCustomButton——VCL 推薦後者,建構子/解構子為虛擬,利於擴展。__closure 讓使用者可指派任意表單方法為事件處理器。pmNotXor 模式下重複繪製同一線段即可擦除,無需 Invalidate 整個控制項,效能極佳。將深入探討 CM_XXXX 訊息處理(如 CM_FONTCHANGED)、屬性編輯器整合、設計時行為客製化。