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

CLinkButton:打造超連結風格扁平化按鈕的完整實現

     編輯:關於VC++

本文介紹 CLinkButton 類別,實現類似超連結的視覺效果:平時外觀如靜態文字,滑鼠懸停時顯示底線與藍色字體並切換手型游標,按下時呈現 3D 邊框下壓效果。完整涵蓋從資源設定到訊息處理的全流程實作。

視覺行為三態設計

狀態外觀描述
Normal純文字,無邊框,類似 CStatic 控制項
Hover文字變藍、加底線、游標變手型、扁平按鈕邊框(凸起/下壓)
Down3D 邊框反轉產生下壓視覺回饋

實作關鍵步驟

1. 資源與類別建立

  • 對話框按鈕屬性勾選「Owner Draw」
  • 匯入手型游標資源
  • 類別精靈建立 CLinkButton 繼承 CButton

2. 重載虛函數與訊息處理宣告

// 虛函數重載
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

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

3. 成員變數定義

CFont fUnderline;      // 底線字型
HCURSOR hHand;         // 手型游標
bool bLBtnDown;        // 按下狀態旗標
bool bHighlight;       // 滑鼠懸停旗標

核心邏輯實現

DrawItem - 完整繪製邏輯

void CLinkButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    CRect rect(lpDrawItemStruct->rcItem);
    COLORREF oc = pDC->GetTextColor();
    int iObk = pDC->SetBkMode(TRANSPARENT);
    UINT state = lpDrawItemStruct->itemState;
    CFont *pOldFont = NULL;
    CString strText; GetWindowText(strText);
    
    if (state & ODS_DISABLED) {
        // 停用狀態:灰顯陰影效果
        CBrush grayBrush; grayBrush.CreateSolidBrush(GetSysColor(COLOR_GRAYTEXT));
        CSize sz = pDC->GetTextExtent(strText);
        int x = rect.left + (rect.Width() - sz.cx)/2;
        int y = rect.top + (rect.Height() - sz.cy)/2;
        rect.OffsetRect(2,2);
        pDC->SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));
        pDC->DrawText(strText, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
        rect.OffsetRect(-2,-2);
        pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
        pDC->DrawText(strText, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
    } else {
        if (bHighlight) {
            // 滑鼠懸停:繪製扁平邊框
            if (state & ODS_SELECTED)
                pDC->Draw3dRect(rect, GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));
            else
                pDC->Draw3dRect(rect, GetSysColor(COLOR_3DHILIGHT), GetSysColor(COLOR_3DSHADOW));
            
            pDC->SetTextColor(RGB(0,0,255));  // 藍色文字
            
            // 動態建立底線字型
            if (fUnderline.GetSafeHandle() == NULL) {
                CFont* pFont = GetFont(); LOGFONT lf;
                pFont->GetLogFont(&lf); lf.lfUnderline = TRUE;
                fUnderline.CreateFontIndirect(&lf);
            }
            pOldFont = pDC->SelectObject(&fUnderline);
        } else {
            pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
        }
        pDC->DrawText(strText, rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
        if (pOldFont) pDC->SelectObject(pOldFont);
    }
}

滑鼠追蹤:Timer + 游標偵測

void CLinkButton::OnMouseMove(UINT nFlags, CPoint point) {
    SetTimer(1, 10, NULL);  // 10ms 定時器輪詢滑鼠位置
    CButton::OnMouseMove(nFlags, point);
}

void CLinkButton::OnTimer(UINT nIDEvent) {
    static bool pPainted = false;
    POINT pt; GetCursorPos(&pt);
    CRect rect; GetWindowRect(rect);
    
    if (bLBtnDown) { KillTimer(1); if(pPainted) InvalidateRect(NULL); pPainted=false; return; }
    
    if (!rect.PtInRect(pt)) {
        bHighlight = false; KillTimer(1);
        if (pPainted) InvalidateRect(NULL); pPainted = false; return;
    } else {
        bHighlight = true;
        if (!pPainted) { pPainted = true; InvalidateRect(NULL); }
    }
    CButton::OnTimer(nIDEvent);
}

BOOL CLinkButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) {
    if (bHighlight) { ::SetCursor(hHand); return true; }
    return CButton::OnSetCursor(pWnd, nHitTest, message);
}

按鈕狀態管理

void CLinkButton::OnLButtonDown(UINT nFlags, CPoint point) {
    bLBtnDown = true; CButton::OnLButtonDown(nFlags, point);
}
void CLinkButton::OnLButtonUp(UINT nFlags, CPoint point) {
    bLBtnDown = false;
    if (bHighlight) { bHighlight = false; InvalidateRect(NULL); }
    CButton::OnLButtonUp(nFlags, point);
}

初始化與背景處理

int CLinkButton::OnCreate(LPCREATESTRUCT lpCreateStruct) {
    if (CButton::OnCreate(lpCreateStruct) == -1) return -1;
    CFont* pFont = GetFont(); LOGFONT lf;
    pFont->GetLogFont(&lf); lf.lfUnderline = TRUE;
    fUnderline.CreateFontIndirect(&lf); return 0;
}

BOOL CLinkButton::OnEraseBkgnd(CDC* pDC) {
    // 繪製略深於系統色的背景,避免閃爍
    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(r<3&&g<3&&b<253) b+=2;
    CRect rc; GetClientRect(rc);
    pDC->FillSolidRect(rc, RGB(r,g,b));
    return CButton::OnEraseBkgnd(pDC);
}

總結

CLinkButton 結合 Owner Draw、Timer 輪詢、動態字型建立、系統色彩整合 等技術,實現了輕量級的超連結風格按鈕。程式碼結構清晰,無外部相依,可直接整合專案使用。

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