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

設計 XP 風格按鈕 CButtonXp:從零打造具熱追蹤效果的自繪按鈕

     編輯:關於VC++

網路上雖有現成的 XP 風格按鈕類別庫,但自己動手實作才能真正掌握核心技術。本文詳細記錄從 CButton 衍生 CButtonXp 的完整過程,實現含滑鼠熱追蹤、按下效果、焦點框、停用狀態的完整按鈕行為。

實作四大關鍵步驟

1. 啟用自繪模式

void CButtonXp::PreSubclassWindow() {
    CButton::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW);  // 關鍵:啟用 Owner Draw
}

2. 滑鼠追蹤與熱效果

使用 TrackMouseEvent() API 實現滑鼠進入/離開偵測:

void CButtonXp::OnMouseMove(UINT nFlags, CPoint point) {
    if (m_bOver == FALSE) {
        m_bOver = TRUE;
        InvalidateRect(NULL, FALSE);  // 觸發重繪
        
        TRACKMOUSEEVENT tme;
        tme.cbSize = sizeof(TRACKMOUSEEVENT);
        tme.dwFlags = TME_LEAVE;
        tme.dwHoverTime = 0;
        tme.hwndTrack = m_hWnd;
        ::TrackMouseEvent(&tme);  // 註冊 WM_MOUSELEAVE
    }
    CButton::OnMouseMove(nFlags, point);
}

// WM_MOUSELEAVE 處理
void CButtonXp::OnMouseOut() {
    m_bOver = FALSE;
    InvalidateRect(NULL, FALSE);
}

需在訊息對應表加入:ON_MESSAGE(WM_MOUSELEAVE, OnMouseOut)

3. 滑鼠狀態查詢介面

BOOL CButtonXp::MouseOver() { return m_bOver; }

4. DrawItem 完整繪圖邏輯

void CButtonXp::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
    CDC *pDC = CDC::FromHandle(lpDIS->hDC);
    CRect rtControl(lpDIS->rcItem);
    CPen pen; CBrush brush; CString strText;
    
    HFONT hOldFont = (HFONT)pDC->SelectObject((HFONT)::GetStockObject(DEFAULT_GUI_FONT));
    UINT state = lpDIS->itemState;
    
    if (state & ODS_FOCUS) rtControl.DeflateRect(1,1);  // 焦點時內縮
    
    // 狀態判斷與顏色設定
    if ((state & ODS_DISABLED) || (!MouseOver() && !(state & ODS_SELECTED))) {
        // 一般/停用/焦點狀態
        pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
        brush.CreateSolidBrush(HLS_TRANSFORM(::GetSysColor(COLOR_3DFACE), -10, 0));
    } else {
        COLORREF crBorder = ::GetSysColor(COLOR_HIGHLIGHT);
        pen.CreatePen(PS_SOLID, 1, crBorder);
        if (state & ODS_SELECTED) {
            // 按下狀態
            brush.CreateSolidBrush(HLS_TRANSFORM(crBorder, +50, -50));
            pDC->SetTextColor(RGB(240,240,240));
        } else {
            // 滑鼠懸停狀態
            brush.CreateSolidBrush(HLS_TRANSFORM(crBorder, +80, -66));
            pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT));
        }
    }
    
    // 文字顏色微調
    if (state & ODS_DISABLED) pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
    else if (state & ODS_SELECTED) pDC->SetTextColor(RGB(240,240,240));
    else if (MouseOver()) pDC->SetTextColor(0);
    else pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT));
    
    // 實際繪製
    CBrush* old_brush = pDC->SelectObject(&brush);
    CPen* old_pen = pDC->SelectObject(&pen);
    pDC->Rectangle(rtControl);
    pDC->SetBkMode(TRANSPARENT);
    GetWindowText(strText);
    pDC->DrawText(strText, rtControl, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
    
    // 焦點虛線框
    if (state & ODS_FOCUS) {
        rtControl.DeflateRect(3,3);
        pDC->DrawFocusRect(rtControl);
    }
    
    // 還原 GDI 物件
    pDC->SelectObject(old_pen);
    pDC->SelectObject(old_brush);
    pDC->SelectObject(hOldFont);
}

必要標頭檔與宣告

#include <winuser.h>
extern "C" WINUSERAPI BOOL WINAPI TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack);

編譯環境與測試

Windows 98 + Visual C++ 6.0 環境下編譯通過。執行效果展示 Normal、Over、Down、Focus、Disable 五種狀態完整切換,視覺貼近 Windows XP 原生風格。

技術重點總結

  • BS_OWNERDRAW 是自繪的前提條件
  • TrackMouseEvent 解決標準訊息無法偵測滑鼠離開的問題
  • HLS_TRANSFORM 巨集依系統色彩動態計算衍生色,適應主題變更
  • 系統色彩常數(COLOR_HIGHLIGHT、COLOR_BTNTEXT 等)確保與系統主題一致
  • GDI 物件嚴格還原 避免資源洩漏

總結

CButtonXp 展示了自繪按鈕的最小完整實現,程式碼精簡易懂,適合作為客製化按鈕開發的基礎範本。原始碼可直接整合專案使用。

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