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

VC++ 純 Win32 API 實作自繪按鈕類別:無 MFC 依賴、支援拖曳與多狀態繪製

     編輯:關於VC++

拒絕 MFC 膨脹,回歸 Win32 API 核心。本文分享一個純 API 實作的自繪按鈕類別 CWINButton,封裝子類化、Thunk 技術、多狀態繪製 (正常/游標懸停/按下/禁用)、圖示/文字/提示/不規則區域/拖曳移動等功能,編譯為動態連結庫 (DLL) 供 MFC/非 MFC 專案共用。

設計核心:子類化 + Thunk 解決成員函式回調

Win32 視窗程序必須是靜態/全域函式,無法直接指向類別成員函式。採用 Thunk 機制 (或 SetProp/GetProp) 將 this 指標與視窗句柄關聯,靜態回調函式內取得 this 再轉呼叫實例成員函式。

關鍵程式碼片段

// 類別宣告節錄 class DLLPORT CWINButton { public: BOOL GetItemhWnd(HWND hWnd); // 初始化:子類化目標按鈕 BOOL Restore(); // 還原原始視窗程序 BOOL SetDrag(BOOL bEnable); // 啟用拖曳移動 BOOL SetIcon(HICON hIcon); BOOL SetText(LPCTSTR pszText, HFONT hFont = NULL); BOOL SetBackPic(HBITMAP hBmp, BOOL bResize); // 背景圖 (可依圖調整按鈕大小) BOOL SetHoverPic(HBITMAP hBmp); // 懸停狀態圖 BOOL SetPressPic(HBITMAP hBmp); // 按下狀態圖 BOOL SetDisablePic(HBITMAP hBmp); // 禁用狀態圖 BOOL SetToolTip(LPCTSTR pszText); BOOL SetupRegion(COLORREF clrTransparent); // 不規則區域 (透明色) // ... private: static LRESULT WINAPI StdProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // 靜態回調入口 LRESULT CALLBACK WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // 實例視窗程序 WNDPROC CreateThunk(); // 建立 Thunk 將 this 綁定到 StdProc // 繪圖輔助 BOOL DrawFlat(HDC hdc, RECT* prc); BOOL DrawInsideBorder(HDC hdc, RECT* prc); // 狀態旗標 BOOL m_bMouseTracking, m_bPress, m_bFocus, m_bEnable, m_bOwnerDraw, m_bDrag, m_bDragEnable; // 視覺資源 HICON m_hIcon; HBITMAP m_hBack, m_hHover, m_hPress, m_hDisable; HFONT m_hFont; COLORREF m_clrText; // ... };

子類化與 Thunk 實作

BOOL CWINButton::GetItemhWnd(HWND hWnd) { m_hWnd = hWnd; m_hWndParent = GetParent(hWnd); // 將 this 存入視窗屬性 if (SetProp(m_hWnd, _T("CWINBUTTON"), (HANDLE)this) == 0) return FALSE; // 替換視窗程序為靜態 StdProc m_OldProc = SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)StdProc); return TRUE; } LRESULT WINAPI CWINButton::StdProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CWINButton* pThis = (CWINButton*)GetProp(hWnd, _T("CWINBUTTON")); if (pThis) return pThis->WndProc(uMsg, wParam, lParam); return DefWindowProc(hWnd, uMsg, wParam, lParam); }

核心繪製邏輯:WM_PAINT / WM_PRINTCLIENT

所有視覺呈現集中在實例 WndProc 的 WM_PAINT 處理:

  1. 取得 HDC (BeginPaint 或 WM_PRINTCLIENT 傳入)。
  2. 依狀態旗標 (m_bPress, m_bMouseTracking, m_bFocus, m_bEnable) 選擇背景圖 (m_hBack/m_hHover/m_hPress/m_hDisable) 或純色填充。
  3. 繪製邊框:平面/3D/內邊框高亮 (游標懸停/焦點時不同顏色筆刷)。
  4. 繪製圖示 (DrawIconEx) 與文字 (DrawText / TextOut),支援置中/偏移排版。
  5. 若 m_bOwnerDraw (使用者設定圖片),依圖片透明色建立不規則區域 (SetupRegion)。

狀態追蹤訊息處理

訊息處理目的
WM_MOUSEMOVETrackMouseEvent(TME_LEAVE) 追蹤游標離開;設 m_bMouseTracking=TRUE,Invalidate() 重繪懸停狀態。
WM_MOUSELEAVE清 m_bMouseTracking,重繪恢復正常狀態。
WM_LBUTTONDOWN設 m_bPress=TRUE,SetCapture(),重繪按下狀態;若啟用拖曳 (m_bDragEnable) 記錄起始座標。
WM_LBUTTONUP清 m_bPress,ReleaseCapture();若在拖曳模式計算位移 MoveWindow,否則發送 BN_CLICKED 通知父視窗。
WM_SETFOCUS / WM_KILLFOCUS設/清 m_bFocus,重繪焦點虛線框。
WM_ENABLE同步 m_bEnable,重繪禁用狀態。
WM_KEYDOWN (VK_SPACE)鍵盤空白鍵模擬點擊,觸發 BM_CLICK。

在 MFC 專案中使用注意事項

DLL 版本按鈕類別會自行處理 WM_PAINT,不需 父視窗處理 WM_DRAWITEM。若父視窗為 MFC CDialog/CWnd,請勿在 OnDrawItem 中呼叫基類實作,直接回傳 TRUE 或完全不處理:

void CMyDialog::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) { // 不要呼叫 CDialog::OnDrawItem! // 我們的按鈕已在子類化中自行繪製 }

否則程式關閉時可能觸發非法操作 (雙重銷毀/重繪衝突)。

優勢總結

  • 零 MFC 依賴:純 SDK,可用於 ATL、WTL、純 API、甚至其他語言 (VB6/Delphi/C# P/Invoke) 專案。
  • 豐富視覺狀態:正常/懸停/按下/禁用/焦點/不規則區域/自訂圖片/文字/圖示/ToolTip 全支援。
  • 拖曳移動:獨特功能,按鈕可在執行時由使用者拖曳重新定位。
  • DLL 部署:編譯為 CWINButton.dll + .lib,專案只需包含標頭檔連結即可。

完整原始碼 (含 Thunk 組合語言實作 x86/x64) 可從 VC 知識庫下載。歡迎技術交流。

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