首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++ Builder 元件開發深度解析(三):VCL 訊息機制進階——CN/CM 訊息與自訂訊息

     編輯:VC 教學

前言

承接前兩篇(繼承體系、繪圖與虛擬函數),本文深入 VCL 內部訊息機制核心:**CN_* / CM_* 非標準訊息**與**自訂訊息/事件**的實現。這是編寫專業級元件、實現設計時期行為、整合 IDE 設計器的關鍵。

一、為什麼需要 CN/CM 訊息?

Windows 標準訊息(WM_COMMAND、WM_NOTIFY、WM_DRAWITEM 等)**發送給父視窗**,而非產生訊息的子控件。VCL 為了讓控件自身處理這些訊息,引入了轉發機制:

  1. 父視窗收到 WM_COMMAND 等。
  2. 在 TWinControl.WndProc 中,將訊息 ID 加上 CN_BASE($B000)形成 CN_* 訊息(如 CN_COMMAND、CN_DRAWITEM)。
  3. 透過 Perform(CN_*, ...) 分發給**實際子控件**的 WndProc。
  4. 子控件可重寫 WndProc 或使用訊息映射處理 CN_*。

同理,VCL 內部狀態變更(Font、Color、Showing、Enabled 等)定義 CM_* 訊息(CM_BASE = $B000),由控件自身或父控件觸發,實現屬性變更的級聯更新。

二、常見 CN/CM 訊息對照表

標準訊息VCL 轉發訊息典型用途
WM_COMMANDCN_COMMAND按鈕點擊、選單命令、編輯變更通知
WM_NOTIFYCN_NOTIFY通用控件通知(ListView、TreeView、ToolTip 等)
WM_DRAWITEMCN_DRAWITEMOwnerDraw 控件繪製(TBitBtn 圖文繪製即處理此訊息)
WM_MEASUREITEMCN_MEASUREITEMOwnerDraw 項目尺寸測量
WM_DELETEITEMCN_DELETEITEMOwnerDraw 項目刪除清理
WM_HSCROLL/VSCROLLCN_HSCROLL/VSCROLL捲軸通知轉發
—CM_FONTCHANGEDFont 屬性變更,觸發 Invalidate 重繪
—CM_COLORCHANGEDColor 變更
—CM_ENABLEDCHANGEDEnabled 狀態變更
—CM_VISIBLECHANGEDVisible 狀態變更
—CM_PARENTFONTCHANGED父控件 Font 變更傳播
—CM_SHOWINGCHANGEDShowing 狀態變更(可見性實際改變)
—CM_TEXTCHANGEDCaption/Text 變更
—CM_DESIGNHITTEST設計時期點擊測試(支援非矩形選取)

三、處理 CN/CM 訊息:訊息映射宏

因無對應虛擬函數,必須用 **VCL_MESSAGE_HANDLER** 宏映射:

class TMyButton : public TButton {
protected:
    BEGIN_MESSAGE_MAP
        VCL_MESSAGE_HANDLER(CN_DRAWITEM, TWMDrawItem, CNDrawItem)
        VCL_MESSAGE_HANDLER(CM_FONTCHANGED, TMessage, CMFontChanged)
    END_MESSAGE_MAP(TButton)

    void __fastcall CNDrawItem(TWMDrawItem &Msg);
    void __fastcall CMFontChanged(TMessage &Msg);
};

實作範例:TBitBtn 的 CN_DRAWITEM

void __fastcall TBitBtn::CNDrawItem(TWMDrawItem &Msg) {
    TDrawItemStruct *dis = (TDRAWITEMSTRUCT)Msg.DrawItemStruct;
    FCanvas->Handle = dis->hDC;
    TRect R = ClientRect;
    // ... 計算圖文佈局、按下偏移、焦點矩形 ...
    TButtonGlyph(FGlyph).Draw(FCanvas, R, Point(0,0), Caption, FLayout,
                              FMargin, FSpacing, State, False,
                              DrawTextBiDiModeFlags(0));
    if (IsFocused && IsDefault) {
        R = ClientRect; InflateRect(R, -4, -4);
        FCanvas->Pen->Color = clWindowFrame;
        FCanvas->Brush->Color = clBtnFace;
        DrawFocusRect(FCanvas->Handle, R);
    }
    FCanvas->Handle = 0;
}

本質上就是在 hDC 上繪圖,與 WM_PAINT 邏輯相似,但針對 OwnerDraw 控件。

四、定義與使用自訂訊息/事件

4.1 事件型別宣告

typedef void __fastcall (__closure *THoverShapeEvent)(TObject *Sender, int Index);
typedef void __fastcall (__closure *TShapeSelectedEvent)(TObject *Sender, int Index);

4.2 元件類別中聲明事件屬性

__published:
    __property THoverShapeEvent OnHoverShape = {read=FOnHoverShape, write=FOnHoverShape};
    __property TShapeSelectedEvent OnShapeSelected = {read=FOnShapeSelected, write=FOnShapeSelected};

4.3 觸發事件

if (FOnHoverShape) FOnHoverShape(this, Index);
if (FOnShapeSelected) FOnShapeSelected(this, Index);

4.4 設計時期事件觸發

在 CM_DESIGNHITTEST 或滑鼠移動處理中,呼叫上述觸發代碼,IDE 設計器即可即時響應(如顯示提示、改變游標)。

五、進階技巧:訊息映射繼承與擴展

  • 子類別訊息映射以 END_MESSAGE_MAP(ParentClass) 指定父類,自動繼承父類映射。
  • 可重寫父類處理函數(同名),或新增映射處理新訊息。
  • 動態建立的控件(非設計時期)同樣走訊息映射,無需額外註冊。

六、總結

掌握 CN/CM 訊息機制,是從「會用元件」進階到「寫出專業元件」的分水嶺。它讓你:

  1. 正確處理 OwnerDraw、通知轉發等 Windows 標準訊息的 VCL 封裝方式。
  2. 響應屬性變更(Font、Color、Enabled...)實現即時視覺更新。
  3. 支援設計時期交互(點擊測試、屬性編輯器聯動)。
  4. 定義優雅的事件介面,無縫融入 IDE 事件驅動開發模式。

下一篇將實戰剖析一個專業級元件完整源碼,並手把手帶你從零編寫一個可用元件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved