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

突破 CToolBar 限制:CMTToolBar 實現工具列按鈕顯示多行文字

     編輯:VC 教學

MFC 標準 CToolBar 僅支援單行文字,若需按鈕顯示「新增\n檔案」這樣的多行文字,必須自繪。本文提供一個完整的 CMTToolBar 類別,核心思路:**將文字渲染成位圖,合成至工具列位圖資源中,替換原位圖**,最終調用 ShowText(UINT nIDResource) 一行搞定。

效果展示

圖一:工具列按鈕顯示多行文字效果 (如「新增\n檔案」)

核心原理:文字轉位圖合成法

  1. 解析工具列資源 (RT_TOOLBAR) 取得原始按鈕位圖與尺寸。
  2. 讀取字串表資源,按 \n 分割多行文字。
  3. 建立記憶體 DC,繪製原始圖示 + 多行文字 (置中、換行) 至新位圖。
  4. 調用 SetSizes 更新按鈕尺寸,AddReplaceBitmap 替換工具列位圖。

關鍵函數 BOOL CMTToolBar::ShowText(UINT nIDResource) 完整實現

BOOL CMTToolBar::ShowText(UINT nIDResource)
{
    // 1. 解析工具列資源結構 (CToolBarData) 取得原始圖示尺寸
    HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(nIDResource), RT_TOOLBAR);
    HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nIDResource), RT_TOOLBAR);
    HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
    CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
    CSize sizeImage(pData->wWidth, pData->wHeight);
    UnlockResource(hGlobal); FreeResource(hGlobal);

    // 2. 取得 ToolBarCtrl DC,建立相容 DC 用於繪製新位圖
    CToolBarCtrl& bar = GetToolBarCtrl();
    CDC* pDC = bar.GetDC();
    CDC dcDst; dcDst.CreateCompatibleDC(pDC);

    // 3. 建立繪文字字體 (宋體 12px)
    LOGFONT lf = {0}; lf.lfHeight = 12; lf.lfCharSet = GB2312_CHARSET; _tcscpy(lf.lfFaceName, _T("宋體"));
    CFont font; font.CreateFontIndirect(&lf);
    CFont* pOldFont = dcDst.SelectObject(&font);

    // 4. 讀取所有按鈕文字 (資源字串表),按 \n 分行,計算所需最大寬高
    int nCount = bar.GetButtonCount();
    CStringArray* pStrArrays = new CStringArray[nCount];
    int nMaxLines = 0, nBtnCount = 0, nCharHeight = 0;
    CSize sizeNewImage(sizeImage.cx, 0);
    TBBUTTON tb;

    for (int i = 0; i < nCount; ++i) {
        bar.GetButton(i, &tb);
        if (tb.fsStyle & TBSTYLE_SEP) continue; // 跳過分隔條
        CString str; str.LoadString(tb.idCommand);
        int nPos = 0;
        while ((nPos = str.Find(_T('\n'), nPos)) != -1) { // 簡化分行邏輯
            // ... 分割字串存入 pStrArrays[nBtnCount] ...
            // 計算 nMaxLines, nCharHeight, sizeNewImage.cx
        }
        nBtnCount++;
    }
    sizeNewImage.cy = nMaxLines * nCharHeight + sizeImage.cy; // 新單按鈕高度 = 圖示高 + 文字區高

    // 5. 載入原始工具列位圖資源 (LR_CREATEDIBSECTION 保證可寫入)
    CBitmap bmpSrc;
    BITMAP bm;
    bmpSrc.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource), 
        IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS));
    bmpSrc.GetBitmap(&bm);
    int nWidth = bm.bmWidth, nHeight = bm.bmHeight;

    // 6. 建立目標位圖 (總寬 = 單按鈕寬 * 按鈕數)
    CBitmap bmpDst;
    bmpDst.CreateCompatibleBitmap(pDC, sizeNewImage.cx * nBtnCount, sizeNewImage.cy);
    CDC dcSrc; dcSrc.CreateCompatibleDC(pDC);
    CBitmap* pOldSrc = dcSrc.SelectObject(&bmpSrc);
    CBitmap* pOldDst = dcDst.SelectObject(&bmpDst);

    // 7. 繪製合成:背景色 -> 原始圖示 (置中) -> 多行文字 (置中)
    dcDst.FillSolidRect(0, 0, sizeNewImage.cx * nBtnCount, sizeNewImage.cy, ::GetSysColor(COLOR_BTNFACE));
    dcDst.SetBkMode(TRANSPARENT);
    int nStartX = (sizeNewImage.cx - sizeImage.cx) / 2;

    for (int i = 0; i < nBtnCount; ++i) {
        // 繪製原始圖示
        dcDst.BitBlt(i * sizeNewImage.cx + nStartX, 0, sizeImage.cx, sizeImage.cy, &dcSrc, i * sizeImage.cx, 0, SRCCOPY);
        // 繪製多行文字
        for (int j = 0; j < pStrArrays[i].GetSize(); ++j) {
            CSize sz = dcDst.GetTextExtent(pStrArrays[i][j]);
            int x = i * sizeNewImage.cx + (sizeNewImage.cx - sz.cx) / 2;
            int y = sizeImage.cy + j * nCharHeight;
            dcDst.TextOut(x, y, pStrArrays[i][j]);
        }
    }

    // 8. 清理 DC,更新工具列尺寸與位圖
    dcSrc.SelectObject(pOldSrc); dcDst.SelectObject(pOldDst); dcDst.SelectObject(pOldFont);
    bar.ReleaseDC(pDC); delete[] pStrArrays;

    SetSizes(sizeNewImage + CSize(7, 7), sizeNewImage); // 更新按鈕/圖示尺寸 (含邊距)
    AddReplaceBitmap((HBITMAP)bmpDst.Detach()); // 替換位圖 (關鍵!)

    return TRUE;
}

使用方法

  1. 資源編輯器開啟工具列資源,按鈕文字屬性中輸入多行文字,行間用 \n 分隔 (如 新增\n檔案)。
  2. 將主框架 m_wndToolBar 類型從 CToolBar 改為 CMTToolBar,含標頭檔。
  3. OnCreate 中建立工具列後調用:m_wndToolBar.ShowText(IDR_MAINFRAME);

資源編輯器設定示意

圖二:工具列按鈕文字屬性中使用 \n 換行

優缺點分析

優點缺點
無需自繪按鈕 (OwnerDraw),相容標準工具列邏輯文字屬於位圖一部分,無法動態變更 (需重新呼叫 ShowText)
支援任意字體、顏色、對齊、換行高 DPI 下位圖可能模糊 (建議改用向量繪製或高解析度位圖)
實現簡單,一行代碼接入不支援圖示+文字混合佈局 (文字固定在圖示下方)

此法適合靜態介面、快速原型、不需頻繁動態切換文字的場景。

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