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

VC++ OwnerDraw 按鈕實現 3D 文字特效完整教學

     編輯:VC 教學

前言

透過 BS_OWNERDRAW 樣式與 CButton::DrawItem 覆寫,可完全自繪按鈕外觀。本文實作 **3D 立體文字按鈕**:陰影/高光多層疊加模擬凸起效果、自動縮放字體適應按鈕大小、支援按下/放開狀態視覺回饋。

步驟 1:資源編輯器建立 OwnerDraw 按鈕

  1. AppWizard 建立 Dialog-based 專案 Test。
  2. 對話框放入 Button 控件,ID: IDC_3DTEXTBTN,Caption: 誰與爭瘋。
  3. Properties → Styles → 勾選 **Owner Draw** (BS_OWNERDRAW)。

步驟 2:ClassWizard 建立 C3dTextButton 類別

  • 基類:CButton。
  • 名稱:C3dTextButton。

步驟 3:核心繪圖邏輯 Draw()


void C3dTextButton::Draw(CDC* pDC, const CRect& rect, UINT state)
{
    CString text; GetWindowText(text);
    int len = text.GetLength();
    CRect rcClient = rect;

    // 1. 取得字體並建立 LOGFONT
    CFont* pFont = GetFont();
    LOGFONT lf; pFont->GetObject(sizeof(LOGFONT), &lf);
    if (lf.lfHeight == 0) lf.lfHeight = 20;
    lf.lfWidth = 0; lf.lfWeight = 1000; // 粗體
    lf.lfEscapement = lf.lfOrientation = 0;

    CFont tryFont; tryFont.CreateFontIndirect(&lf);
    CFont* pOldFont = pDC->SelectObject(&tryFont);

    // 2. 自動縮放字體高度適應按鈕
    CSize sz = pDC->GetTextExtent(text, len);
    if (rcClient.Width() * sz.cy > rcClient.Height() * sz.cx) {
        lf.lfHeight = MulDiv(lf.lfHeight, rcClient.Height(), sz.cy);
    } else {
        lf.lfHeight = MulDiv(lf.lfHeight, rcClient.Width(), sz.cx);
    }

    CFont font; font.CreateFontIndirect(&lf);
    pDC->SelectObject(&font);
    sz = pDC->GetTextExtent(text, len);

    // 3. 計算文字置中位置
    int minX = rcClient.left + (rcClient.Width() - sz.cx) / 2;
    int minY = rcClient.top  + (rcClient.Height() - sz.cy) / 2;

    int oldBkMode = pDC->SetBkMode(TRANSPARENT);
    COLORREF oldTextColor = pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT));

    int cx = minX, cy = minY;
    int s = (state & ODS_SELECTED) ? -1 : +1; // 按下時陰影反向
    cx += 3; cy += 3; // 基準偏移

    // 4. 繪製 3D 效果:多層偏移文字疊加
    // 暗影層 (最外層)
    pDC->SetTextColor(::GetSysColor(COLOR_3DDKSHADOW));
    pDC->TextOut(cx - s*2, cy + s*2, text);
    pDC->TextOut(cx + s*2, cy - s*2, text);
    pDC->TextOut(cx + s*2, cy + s*2, text);

    // 高光層
    pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
    pDC->TextOut(cx + s*1, cy - s*2, text);
    pDC->TextOut(cx - s*2, cy + s*1, text);
    pDC->TextOut(cx - s*2, cy - s*2, text);

    // 陰影層
    pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
    pDC->TextOut(cx - s*1, cy + s*1, text);
    pDC->TextOut(cx + s*1, cy - s*1, text);
    pDC->TextOut(cx + s*1, cy + s*1, text);

    // 邊緣高光
    pDC->SetTextColor(::GetSysColor(COLOR_3DLIGHT));
    pDC->TextOut(cx, cy - s*1, text);
    pDC->TextOut(cx - s*1, cy, text);
    pDC->TextOut(cx - s*1, cy - s*1, text);

    // 5. 繪製主文字
    pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT));
    pDC->TextOut(cx, cy, text);

    // 6. 還原 DC 狀態
    pDC->SetTextColor(oldTextColor);
    pDC->SetBkMode(oldBkMode);
    pDC->SelectObject(pOldFont);
}

步驟 4:覆寫 DrawItem


void C3dTextButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    ASSERT_VALID(pDC);
    CRect rcClient = lpDrawItemStruct->rcItem;
    Draw(pDC, rcClient, lpDrawItemStruct->itemState);
}

步驟 5:對話框關聯控件變數

  1. ClassWizard → Member Variables → IDC_3DTEXTBTN → Add Variable → Type: C3dTextButton, Name: m_3dTextButton1。
  2. 在對話框標頭檔 #include "3dTextButton.h"。

關鍵技術點解析

技術點說明
BS_OWNERDRAW告知系統不繪製標準按鈕,改由 DrawItem 自繪。
DrawItem 參數rcItem 為按鈕矩形,itemState 包含 ODS_SELECTED (按下)、ODS_FOCUS 等。
系統色彩使用 GetSysColor(COLOR_3D...) 自動適應系統主題 (經典/視覺風格/高對比度)。
字體自動縮放MulDiv 依按鈕寬高比例調整 lfHeight,確保文字填滿不溢出。
3D 疊加原理以基準位置 (cx,cy) 為中心,按下狀態 s=-1 反向偏移,疊加 4 組系統色彩 (暗影/高光/陰影/邊緣) 模擬立體感。
DC 狀態還原儲存/還原 BkMode, TextColor, Font,避免污染後續繪圖。

現代化改良建議

  • 使用 **GDI+** GraphicsPath + LinearGradientBrush 繪製真正向量 3D 文字,支援抗鋸齒、透明度、縮放不失真。
  • 改用 **WM_PAINT** + CPaintDC 而非 DrawItem (需 BS_OWNERDRAW 配合 WM_PRINTCLIENT),獲得更完整繪圖控制。
  • 高 DPI 環境下,GetSysColor 色彩正確,但像素座標需配合 DPI 感知縮放。

總結

OwnerDraw 按鈕給予完全繪圖控制權。本文實作的 3D 文字按鈕,結合**系統色彩主題適應**、**自動字體縮放**、**按下狀態視覺反饋**、**多層文字疊加模擬立體**,展示了 GDI 繪圖的經典技巧。現代專案建議遷移至 GDI+/Direct2D 獲得更佳視覺品質與高 DPI 支援。

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