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

從零打造 XP 風格按鈕:CButtonXp 自繪實現全解析 (含鼠標追蹤技巧)

     編輯:VC 教學

想擁有 XP 風格按鈕 (滑鼠經過變藍、按下凹陷、焦點虛線框),但不想依賴第三方巨型類別庫?本文教你從 CButton 派生 CButtonXp,僅用 WM_MOUSEMOVE + TrackMouseEvent + DrawItem 三大核心,完整實現專業級 XP 風格按鈕。

效果展示

四大步驟核心實現

1. 派生類別並啟用 Owner-Draw

// CButtonXp.h
class CButtonXp : public CButton
{
    BOOL m_bOver = FALSE; // 滑鼠是否在按鈕區域
protected:
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg LRESULT OnMouseOut(WPARAM, LPARAM); // WM_MOUSELEAVE
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
    DECLARE_MESSAGE_MAP()
};

// CButtonXp.cpp
void CButtonXp::PreSubclassWindow()
{
    ModifyStyle(0, BS_OWNERDRAW); // 關鍵:啟用自繪
    CButton::PreSubclassWindow();
}

2. 滑鼠追蹤:TrackMouseEvent 實現 WM_MOUSELEAVE

MFC 無內建 WM_MOUSELEAVE,需手動註冊追蹤:

void CButtonXp::OnMouseMove(UINT nFlags, CPoint point)
{
    if (!m_bOver) // 首次進入
    {
        m_bOver = TRUE;
        InvalidateRect(NULL, FALSE); // 觸發重繪 (進入態)

        // 註冊離開追蹤
        TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0 };
        ::TrackMouseEvent(&tme); // 需 #include  與 extern "C" 宣告
    }
    CButton::OnMouseMove(nFlags, point);
}

// 訊息映射需手動添加:ON_MESSAGE(WM_MOUSELEAVE, OnMouseOut)
LRESULT CButtonXp::OnMouseOut(WPARAM, LPARAM)
{
    m_bOver = FALSE;
    InvalidateRect(NULL, FALSE); // 觸發重繪 (離開態)
    return 0;
}

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

3. 核心繪製 DrawItem:五大狀態完整邏輯

void CButtonXp::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
    CRect rc(lpDIS->rcItem);
    UINT state = lpDIS->itemState;

    // 字體與焦點矩形調整
    HFONT hOldFont = (HFONT)pDC->SelectObject((HFONT)::GetStockObject(DEFAULT_GUI_FONT));
    if (state & ODS_FOCUS) rc.DeflateRect(1, 1);

    CPen pen, *pOldPen; CBrush brush, *pOldBrush;
    CString strText; GetWindowText(strText);

    // --- 狀態判斷與筆刷/筆/文字顏色設定 ---
    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));
        pDC->SetTextColor(state & ODS_DISABLED ? ::GetSysColor(COLOR_GRAYTEXT) : ::GetSysColor(COLOR_BTNTEXT));
    }
    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)); // 黑字
        }
    }

    // --- 繪製流程 ---
    pDC->SelectObject(&brush); pDC->SelectObject(&pen);
    pDC->Rectangle(rc); // 畫圓角矩形 (此處為直角,可改 RoundRect)
    pDC->SetBkMode(TRANSPARENT);
    pDC->DrawText(strText, rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

    // 焦點虛線框
    if (state & ODS_FOCUS) { rc.DeflateRect(3, 3); pDC->DrawFocusRect(rc); }

    // 還原
    pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); pDC->SelectObject(hOldFont);
}

4. 關鍵輔助函數 HLS_TRANSFORM

代碼中使用的 HLS_TRANSFORM(color, hueShift, lumShift, satShift) 為自定義宏/函數,將 RGB 轉 HLS 調整後再轉回 RGB,用於動態生成按下/懸停態顏色。若無此函數,可直接指定具體 RGB 值 (如按下態 RGB(0, 0, 180)),略失動態適應主題能力。

編譯環境與注意事項

  • 測試環境:Win98 + VC6.0 (現代系統同樣適用)。
  • 需在 stdafx.h 或 .cpp 頂部加入:#include 與 extern "C" WINUSERAPI BOOL WINAPI TrackMouseEvent(LPTRACKMOUSEEVENT);。
  • TrackMouseEvent 在 Win98/NT4 需安裝 IE4+ 或單獨更新 DLL,Win2000+ 內建。

擴展建議

  • 加入 TME_HOVER 旗標與 WM_MOUSEHOVER 處理,實現延遲進入效果。
  • 使用 RoundRect 或 GraphicsPath 繪製真圓角矩形。
  • 支援圖示繪製 (DrawState 或 CImageList::Draw)。
  • 封裝為模板類別支援 CButton, CBitmapButton 等基類。

這個 CButtonXp 雖簡潔,卻包含了自繪按鈕的所有核心要素:狀態機、滑鼠追蹤、系統色彩適應、GDI 繪圖流程,是學習 MFC 自繪控制項的極佳入門範例。

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