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

自繪按鈕深度實現:CXPButton 類設計全紀錄 (含 PreSubclassWindow、DrawItem、滑鼠懸停追蹤)

     編輯:VC 教學

想在程式中展現獨特視覺風格,標準控制項遠遠不夠。本文將帶你從零設計一個 CXPButton 類別,完整涵蓋:**五大狀態視覺設計、Owner-Draw 啟用、滑鼠懸停/離開追蹤 (WM_MOUSEHOVER/LEAVE)、DrawItem 核心繪製邏輯、非客戶區繪製細節** 等自繪按鈕開發的完整知識體系。

一、視覺設計先行:五大狀態外觀定義

動碼前先設計,參考 Windows XP 原生按鈕風格定義:

  • Normal:初始狀態,淺灰背景、立體邊框、黑字。
  • Over (Hot):滑鼠進入,藍色邊框、淺藍漸層背景、黑字。
  • Down (Pressed):按下,邊框內陷、深藍背景、白字、文字向右下位移 1px 產生按壓感。
  • Focus:擁有焦點 (Tab 鍵導航),內縮虛線框 (DrawFocusRect)。
  • Disabled:灰色邊框、極淡背景、灰字 (系统 COLOR_GRAYTEXT)。

設計草圖:五大狀態視覺規範

二、類別建立與成員變量規劃

ClassView 右鍵 Add Class,派生 CButton,命名 CXPButton。成員變量集中於繪圖資源 (筆、刷、字體、區域) 於建構函數建立、解構函數釋放,詳見附件源碼。

三、核心實現四大支柱

1. PreSubclassWindow:啟用 Owner-Draw 屬性

void CXPButton::PreSubclassWindow()
{
    CButton::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW); // 激活自繪,框架將呼叫 DrawItem
}

2. 滑鼠追蹤:WM_MOUSEMOVE 觸發 TrackMouseEvent 獲取 HOVER/LEAVE

void CXPButton::OnMouseMove(UINT nFlags, CPoint point)
{
    if (!m_bTracking) // 避免重複註冊
    {
        TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE | TME_HOVER, m_hWnd, 1 }; // 1ms 延遲觸發 HOVER
        m_bTracking = _TrackMouseEvent(&tme);
    }
    CButton::OnMouseMove(nFlags, point);
}

// 手動添加訊息映射 (ClassWizard 不支援):
// ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
// ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)

afx_msg LRESULT OnMouseLeave(WPARAM, LPARAM);
afx_msg LRESULT OnMouseHover(WPARAM, LPARAM);

於 OnMouseLeave 設 m_bOver=FALSE、Invalidate;OnMouseHover 設 m_bOver=TRUE、Invalidate。MouseOver() 供 DrawItem 查詢。

3. DrawItem:自繪核心,狀態機驅動視覺渲染

此函數為虛函數,ClassView → 右鍵類別 → Add Virtual Function → 選 DrawItem 添加。核心邏輯:

  • 取得 CDC*、CRect、itemState。
  • 依狀態 (ODS_SELECTED, ODS_HOTLIGHT, ODS_DISABLED, ODS_FOCUS) 選擇筆刷、顏色、文字偏移。
  • 繪製背景矩形 → 繪製文字 (置中/偏移) → 繪製焦點虛線框。
  • 嚴格還原 GDI 物件 (SelectObject 舊筆/刷/字體)。

完整代碼較長,請參考附件源碼 XPButton.cpp。

4. 非客戶區繪製 (進階):WM_NCPAINT 實現邊框陰影等效果

若需在標準按鈕邊框外繪製 (如外發光、陰影),需重寫 OnNcPaint 並攔截 WM_NCPAINT。本範例專注客戶區繪製,非客戶區保留系統默認。

兩大難點深度解析

難點解決方案
WM_MOUSELEAVE/HOVER 非標準訊息,ClassWizard 無法添加在 WM_MOUSEMOVE 中呼叫 TrackMouseEvent(TME_LEAVE|TME_HOVER) 由系統產生;訊息映射手寫 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)。
DRAWITEMSTRUCT 結構體資訊解讀複雜重點欄位:hDC (繪圖 DC)、rcItem (項目矩形)、itemState (狀態旗標組合)、itemAction (繪製動作:ODA_DRAWENTIRE/SELECT/FOCUS)、itemID (控制項 ID)。

類別精靈添加虛函數技巧

Add Class 對話框設定

Add Virtual Function 添加 DrawItem

總結

CXPButton 完整展示了 MFC 自繪按鈕的標準開發流程:設計狀態 → 啟用 OwnerDraw → 追蹤滑鼠狀態 → DrawItem 狀態機渲染 → 資源管理。源碼結構清晰,可直接整合至專案作為基礎按鈕類別延伸開發 (如加入圖示、動畫、主題支援)。

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