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

Win32 自繪按鈕進階實作:GDI+ 繪圖與滑鼠進入/離開視覺回饋

     編輯:關於C語言

Win32 自繪按鈕進階實作:GDI+ 繪圖與滑鼠互動特效

本文示範如何在 Win32 應用程式中使用 GDI+ 實作自繪按鈕,並透過 WM_MOUSEHOVER/WM_MOUSELEAVE 訊息實現滑鼠進入時切換圖片、離開時恢復的視覺回饋效果。

核心技術點

  • BS_OWNERDRAW:按鈕樣式標誌,讓父視窗接收 WM_DRAWITEM 負責繪製。
  • 子類別化:SetWindowLongPtr(GWL_WNDPROC) 截獲按鈕訊息,處理滑鼠懸停/離開。
  • TRACKMOUSEEVENT:向系統登錄追蹤旗標 TME_HOVER | TME_LEAVE,觸發 WM_MOUSEHOVER/LEAVE。
  • GDI+ 繪圖:Graphics、Image、LinearGradientBrush、SolidBrush、Pen 實現向量繪圖與 PNG 圖片渲染。

關鍵程式碼片段

1. 視窗建立與按鈕子類別化

// WinMain 內初始化 GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

// WM_CREATE 內建立自繪按鈕
hMainPage = CreateWindow(
    L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
    10, 32, 20, 20, hWnd, (HMENU)ID_MAINPAGE, hInst, NULL);

// 子類別化:攔截按鈕訊息
oldBtnProc = (WNDPROC)SetWindowLongPtr(hMainPage, GWLP_WNDPROC, (LONG_PTR)BtnProc);

2. WM_DRAWITEM 繪製邏輯

case WM_DRAWITEM: {
    LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT)lParam;
    Graphics graphics(pdis->hDC);

    if (pdis->CtlID == ID_MAINPAGE) {
        // 依 btnEntry 旗標切換圖片
        Image *img = btnEntry ? L"bitmap2.png" : L"bitmap.png";
        graphics.DrawImage(img, 0, 0, 20, 20);
    }
    // 其他按鈕繪製漸層背景、幾何圖形...
    // 焦點矩形
    if (pdis->itemState & ODS_FOCUS) {
        pdis->rcItem.left += cx/16; // 縮進
        DrawFocusRect(pdis->hDC, &pdis->rcItem);
    }
    return TRUE;
}

3. 按鈕子類別化程序:滑鼠追蹤

LRESULT CALLBACK BtnProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_MOUSEMOVE:
        if (!btnEntry) {
            TRACKMOUSEEVENT tme = { sizeof(tme), TME_HOVER | TME_LEAVE, 1, hwnd };
            TrackMouseEvent(&tme); // 只需註冊一次
        }
        break;
    case WM_MOUSEHOVER:
        btnEntry = TRUE;
        InvalidateRect(hwnd, NULL, TRUE); // 觸發重繪
        UpdateWindow(hwnd);
        break;
    case WM_MOUSELEAVE:
        btnEntry = FALSE;
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;
    default:
        return CallWindowProc(oldBtnProc, hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

完整專案結構提示

  • 資源檔:bitmap.png (正常態)、bitmap2.png (Hover 態),置於執行檔同目錄。
  • 連結選項:#pragma comment(lib, "Gdiplus.lib"),並定義 _WIN32_WINNT 0x0500 以上。
  • 記得在 WinMain 結束前呼叫 GdiplusShutdown(gdiplusToken)。

常見坑與優化方向


掌握 BS_OWNERDRAW + 子類別化 + GDI+ 組合技,即可在不引入 MFC/WTL/Qt 的純 Win32 專案中實現現代化 UI 控制項。後續可延伸至任意形狀按鈕、動畫過渡、觸控支援等進階主題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題解決/優化
圖片重複載入導致效能差啟動時 Image::FromFile 載入快取,WM_DESTROY 釋放
高 DPI 模糊Manifest 宣告 dpiAware,或使用 SetProcessDpiAwarenessContext
閃爍雙緩衝:BufferedPaint 或記憶體 DC 繪製後 BitBlt
多按鈕狀態管理封裝 COwnerDrawButton 類別,內部維護 btnEntry、圖片指標