開發可在 IDE 設計期操作的 VCL 元件,需遵循嚴格的類別結構、屬性/事件宣告模式與註冊流程。本文系統整理 PACKAGE 類別宣告、__property 讀寫存取器、__published 設計期可見性、事件型別定義與觸發、RegisterComponents 封裝安裝全細節,並給出標準範本程式碼。
class PACKAGE TMyComponent : public TComponent { // 或 TGraphicControl, TCustomControl
private:
// 1. 內部實作細節
// 2. 屬性備份欄位 (F開頭)
AnsiString FCaption;
// 3. 事件備份欄位
TNotifyEvent FOnClick;
// 4. 祖先類型別名,便於呼叫 inherited
typedef TComponent Inherited;
// 私有方法:僅內部使用,盡量寫成通用工具函數
void InternalUpdate();
protected:
// 受保護虛擬方法:派生類可重寫並決定是否公開
virtual void __fastcall Loaded(); // 元件載入完成
virtual void __fastcall Paint(); // 繪圖 (TGraphicControl 以上)
// 事件觸發器:命名慣例 Do + 事件名
virtual void __fastcall DoClick(TObject* Sender) {
if (FOnClick) FOnClick(Sender); // 安全觸發
}
public:
__fastcall TMyComponent(TComponent* Owner);
__fastcall ~TMyComponent();
// 公開方法:執行期 API
void RefreshData();
__published:
// 設計期可見屬性:物件檢視器可編輯、屬性編輯器支援
__property AnsiString Caption = {
read=FCaption, write=FCaption, default="MyComponent"
};
// 或使用存取器方法 (含副作用邏輯)
__property AnsiString Text = {
read=GetText, write=SetText, stored=true, default=""
};
// 設計期可見事件
__property TNotifyEvent OnClick = { read=FOnClick, write=FOnClick };
};
__property Type Name = {
read = FieldOrMethod, // 必填
write = FieldOrMethod, // 可省略 (唯讀)
default = Value, // 預設值,影響 DFM 儲存 (不存預設值)
stored = True/False, // 是否存入 DFM
nodefault, // 無預設值
index = Integer, // 共用存取器時的索引
implements = Interface // 介面委派 (進階)
};
private:
AnsiString FText;
AnsiString __fastcall GetText() { return FText; }
void __fastcall SetText(const AnsiString& Value) {
if (FText != Value) {
FText = Value;
InternalUpdate(); // 副作用:重繪、重算、通知...
}
}
__published:
__property AnsiString Text = { read=GetText, write=SetText };
Sender)__property TNotifyEvent OnChange = { read=FOnChange, write=FOnChange };
// TNotifyEvent = void __fastcall (__closure *)(TObject* Sender)
// 1. 定義事件型別 (類似函數指標)
typedef void __fastcall (__closure *TKeyEvent)(TObject* Sender, WORD& Key, TShiftState Shift);
// 2. 私有備份欄位
TKeyEvent FOnKeyDown;
// 3. protected 觸發器 (虛擬,可被覆寫)
virtual void __fastcall DoKeyDown(TObject* Sender, WORD& Key, TShiftState Shift) {
if (FOnKeyDown) FOnKeyDown(Sender, Key, Shift);
}
// 4. __published 發布
__property TKeyEvent OnKeyDown = { read=FOnKeyDown, write=FOnKeyDown };
DoKeyDown(...)DoChange(this)// 靜態檢查:確保無純虛擬函數,可實例化
static inline void ValidCtrCheck(TMyComponent*) {
new TMyComponent(NULL); // 僅編譯期檢查,不會真正執行
}
namespace Mycomponent { // 命名空間 = 檔名小寫 (慣例)
void __fastcall PACKAGE Register() {
TComponentClass classes[1] = { __classid(TMyComponent) };
RegisterComponents("My Components", classes, 0); // 頁面名稱、類別陣列、索引
}
}
PACKAGE 關鍵字確保編譯為設計期套件 (.bpl)MyComponent.cpp → namespace Mycomponent)File → New → Package).cpp)vcl, rtl, designide (設計期介面)MyPackage.bpl + MyPackage.dcp.bpl 安裝至 IDE| 主題 | 關鍵點 |
|---|---|
| 屬性編輯器 | 繼承 TPropertyEditor,RegisterPropertyEditor 註冊,支援下拉、對話框、子屬性 |
| 元件編輯器 | 繼承 TComponentEditor,右鍵選單加入動詞、雙擊預設動作 |
| 設計期繪圖 | 覆寫 Paint,使用 Canvas 繪製非視窗控制項外觀 |
| 線上說明整合 | HelpKeyword, HelpContext 屬性關聯 CHM 主題 |
| 版本資訊 | GetPackageInfo 讀取 BPL 版本、描述、作者 |
inherited、設定預設尺寸/色彩PACKAGE 宏於專案選項 Package 頁面自動處理C++Builder 元件開發核心在於:PACKAGE 類別、__property 存取器封裝副作用、事件型別+觸發器分離、__published 設計期可見性、RegisterComponents 封裝安裝。掌握此標準流程,即可將任何演算法、硬體介面、第三方庫包裝為可視化拖放元件,大幅提升團隊復用效率。