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

VC++ 6.0 擴充 CButton 實作超連結風格扁平按鈕:CLinkButton 滑鼠懸停變色、下劃線、手型游標、按下陷入效果

     編輯:VC 教學

前言

標準 `CButton` 外觀固定,難以實現網頁超連結風格:平時像靜態文字,滑鼠懸停變藍+下劃線+手型游標,按下有陷入視覺回饋。本文實作 `CLinkButton` 繼承 `CButton`,設定 `BS_OWNERDRAW` 自繪,處理 `WM_MOUSEMOVE`/`WM_LBUTTONDOWN/UP`/`WM_TIMER`/`WM_SETCURSOR`/`WM_ERASEBKGND`,完整呈現三態視覺狀態,零外部依賴。

一、資源設定

  1. 對話方塊編輯器放置 Button,屬性 **Owner Draw = True**(`BS_OWNERDRAW`)。
  2. 匯入手型游標資源(`.cur`),ID = `IDC_CURSOR_HAND`。
  3. ClassWizard 建立 `CLinkButton` 繼承 `CButton`,關聯控制項變數。

二、類別宣告


class CLinkButton : public CButton
{
    DECLARE_DYNAMIC(CLinkButton)
public:
    CLinkButton();
    virtual ~CLinkButton();

protected:
    CFont m_fontUnderline;    // 下劃線字體
    HCURSOR m_hHandCursor;    // 手型游標
    BOOL m_bHighlight = FALSE; // 滑鼠懸停
    BOOL m_bLBtnDown = FALSE;  // 按下狀態

    // 核心繪製
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);

    // 訊息處理
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg);
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);

    DECLARE_MESSAGE_MAP()
};

三、核心繪製:DrawItem


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

    CString str; GetWindowText(str);
    COLORREF clrText;
    CFont* pOldFont = NULL;

    // 背景透明
    pDC->SetBkMode(TRANSPARENT);

    if (state & ODS_DISABLED)
    {
        // 禁用狀態:灰白陰影文字
        CBrush grayBrush(GetSysColor(COLOR_GRAYTEXT));
        CSize sz = pDC->GetTextExtent(str);
        int x = rc.left + (rc.Width() - sz.cx) / 2;
        int y = rc.top + (rc.Height() - sz.cy) / 2;
        rc.OffsetRect(2,2); pDC->SetTextColor(GetSysColor(COLOR_3DHILIGHT)); pDC->DrawText(str, rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
        rc.OffsetRect(-2,-2); pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); pDC->DrawText(str, rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
    }
    else
    {
        if (m_bHighlight)
        {
            // 懸停/按下:繪製 3D 邊框
            if (state & ODS_SELECTED) // 按下
                pDC->Draw3dRect(rc, GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));
            else // 懸停未按下
                pDC->Draw3dRect(rc, GetSysColor(COLOR_3DHILIGHT), GetSysColor(COLOR_3DSHADOW));

            // 藍字 + 下劃線
            clrText = RGB(0,0,255);
            if (m_fontUnderline.GetSafeHandle() == NULL)
            {
                CFont* pFont = GetFont(); LOGFONT lf; pFont->GetLogFont(&lf);
                lf.lfUnderline = TRUE; m_fontUnderline.CreateFontIndirect(&lf);
            }
            pOldFont = pDC->SelectObject(&m_fontUnderline);
        }
        else
        {
            // 正常:普通按鈕文字色
            clrText = GetSysColor(COLOR_BTNTEXT);
        }
        pDC->SetTextColor(clrText);
        pDC->DrawText(str, rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
        if (pOldFont) pDC->SelectObject(pOldFont);
    }
}

四、滑鼠追蹤與狀態機


BEGIN_MESSAGE_MAP(CLinkButton, CButton)
    ON_WM_MOUSEMOVE()
    ON_WM_SETCURSOR()
    ON_WM_TIMER()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_WM_CREATE()
    ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

void CLinkButton::OnMouseMove(UINT nFlags, CPoint point)
{
    SetTimer(1, 10, NULL); // 10ms 輪詢滑鼠是否移出
    CButton::OnMouseMove(nFlags, point);
}

void CLinkButton::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent != 1) return;

    if (m_bLBtnDown) { KillTimer(1); return; } // 按下期間不檢測移出

    CPoint pt; GetCursorPos(&pt);
    CRect rc; GetWindowRect(rc);
    if (!rc.PtInRect(pt))
    {
        // 滑鼠移出
        if (m_bHighlight)
        {
            m_bHighlight = FALSE;
            KillTimer(1);
            Invalidate(); // 重繪恢復正常狀態
        }
    }
    else
    {
        // 滑鼠在內部
        if (!m_bHighlight)
        {
            m_bHighlight = TRUE;
            Invalidate(); // 重繪進入懸停狀態
        }
    }
    CButton::OnTimer(nIDEvent);
}

BOOL CLinkButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg)
{
    if (m_bHighlight)
    {
        ::SetCursor(m_hHandCursor);
        return TRUE;
    }
    return CButton::OnSetCursor(pWnd, nHitTest, msg);
}

void CLinkButton::OnLButtonDown(UINT nFlags, CPoint point)
{
    m_bLBtnDown = TRUE;
    Invalidate(); // 進入按下視覺
    CButton::OnLButtonDown(nFlags, point);
}

void CLinkButton::OnLButtonUp(UINT nFlags, CPoint point)
{
    m_bLBtnDown = FALSE;
    if (m_bHighlight) { m_bHighlight = FALSE; Invalidate(); }
    CButton::OnLButtonUp(nFlags, point);
}

int CLinkButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CButton::OnCreate(lpCreateStruct) == -1) return -1;
    // 載入手型游標
    m_hHandCursor = AfxGetApp()->LoadStandardCursor(IDC_HAND); // 或 LoadCursor(IDC_CURSOR_HAND)
    // 預建立下劃線字體
    CFont* pFont = GetFont(); LOGFONT lf; pFont->GetLogFont(&lf);
    lf.lfUnderline = TRUE; m_fontUnderline.CreateFontIndirect(&lf);
    return 0;
}

BOOL CLinkButton::OnEraseBkgnd(CDC* pDC)
{
    // 繪製稍深於 3D Face 的背景,避免與對話框背景同色顯得突兀
    COLORREF cr = GetSysColor(COLOR_3DFACE);
    int r = GetRValue(cr), g = GetGValue(cr), b = GetBValue(cr);
    if (r > 1) r -= 2; if (g > 1) g -= 2; if (b < 253) b += 2;
    pDC->FillSolidRect(CRect(0,0,0,0), RGB(r,g,b)); // 實際應用 GetClientRect
    return TRUE; // 已處理背景,不呼叫基類
}

五、使用範例


void CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_linkBtn.SubclassDlgItem(IDC_BUTTON_LINK, this);
    m_linkBtn.SetWindowText(_T("訪問官方網站"));
    // 字體可在資源編輯器設定,或此處 SetFont
}

六、執行效果

正常狀態

懸停狀態

按下狀態

平時純文字,懸停變藍+下劃線+手型游標+凸起邊框,按下陷入邊框,釋放觸發 `BN_CLICKED` 命令。

七、結語

`CLinkButton` 以 `BS_OWNERDRAW` + 狀態機實現超連結風格按鈕,核心邏輯:Timer 輪詢滑鼠位置 → m_bHighlight/m_bLBtnDown 驅動 DrawItem 三態繪製 → OnSetCursor 切換手型。無圖片資源依賴、純系統色適應主題、支援禁用狀態。可直接納入共用控制項庫,替代傳統 `CButton` 用於說明連結、啟動外部 URL、觸發精靈等動作,提升介面現代感。

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