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

MFC 工具欄動態添加標準控件(ComboBox、Edit 等)完整實現

     編輯:VC 教學

需求背景

MFC 默認工具欄(CToolBar)僅支持按鈕。專業應用(如 Visual Studio、Office)工具欄常包含組合框、編輯框、下拉按鈕等標準控件。本文介紹在工具欄預留佔位按鈕 → 動態創建子控件 → 消息映射響應的通用實現模式。

核心思路

  1. 資源編輯器為工具欄添加**分隔符類型**按鈕(ID 如 IDC_TOOL_COMBO),作為佔位符
  2. CToolBar::SetButtonInfo 設置佔位按鈕寬度、風格(TBBS_SEPARATOR)
  3. CToolBar::GetItemRect 獲取佔位按鈕矩形區域
  4. 調整矩形(如增加下拉高度),調用控件 Create 在工具欄內創建子窗口
  5. 手動添加消息映射宏響應控件通知(ON_CBN_SELCHANGE 等)

完整代碼實現

1. 主框架類成員變量聲明


// MainFrm.h
class CMainFrame : public CFrameWnd
{
    // ...
protected:
    CToolBar    m_wndToolBar;
    CComboBox   m_wndComboBox;   // 工具欄組合框
    CEdit       m_wndEdit;       // 工具欄編輯框
    // ...
};

2. OnCreate 中創建工具欄與動態控件


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;

    // --- 標準工具欄創建 ---
    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
        | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
        !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
    {
        TRACE0("工具欄創建失敗\n");
        return -1;
    }

    // --- 動態創建組合框 ---
    // 1. 設置佔位按鈕(索引 8,假設資源編輯器中第 9 個按鈕為分隔符)
    m_wndToolBar.SetButtonInfo(8, IDC_TOOL_COMBO, TBBS_SEPARATOR, 160); // 寬度 160 像素

    // 2. 獲取佔位按鈕矩形
    CRect rectCombo;
    m_wndToolBar.GetItemRect(8, &rectCombo);
    
    // 3. 擴展底部高度以容納下拉列表
    rectCombo.bottom += 100; 

    // 4. 創建組合框(CBS_DROPDOWN 允許編輯,CBS_DROPDOWNLIST 僅選擇)
    if (!m_wndComboBox.Create(
        CBS_DROPDOWN | WS_VISIBLE | WS_TABSTOP | CBS_AUTOHSCROLL | WS_VSCROLL,
        rectCombo, &m_wndToolBar, IDC_TOOL_COMBO))
    {
        TRACE0("組合框創建失敗\n");
        return -1;
    }

    // 5. 添加初始項目
    m_wndComboBox.AddString(_T("第一條記錄"));
    m_wndComboBox.AddString(_T("第二條記錄"));
    m_wndComboBox.AddString(_T("第三條記錄"));
    m_wndComboBox.SetCurSel(0);

    // --- 動態創建編輯框 ---
    m_wndToolBar.SetButtonInfo(9, IDC_TOOL_EDIT, TBBS_SEPARATOR, 120);
    CRect rectEdit;
    m_wndToolBar.GetItemRect(9, &rectEdit);
    
    if (!m_wndEdit.Create(
        WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL,
        rectEdit, &m_wndToolBar, IDC_TOOL_EDIT))
    {
        TRACE0("編輯框創建失敗\n");
        return -1;
    }
    m_wndEdit.SetWindowText(_T("默認文本"));

    return 0;
}

3. 手動添加消息映射響應控件事件

因動態創建控件,ClassWizard 無法自動生成映射,需手寫:


// MainFrm.cpp
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    // ...
    // 組合框事件
    ON_CBN_SELCHANGE(IDC_TOOL_COMBO, OnSelChangeToolCombo)
    ON_CBN_EDITCHANGE(IDC_TOOL_COMBO, OnEditChangeToolCombo)
    ON_CBN_DROPDOWN(IDC_TOOL_COMBO, OnDropDownToolCombo)
    ON_CBN_CLOSEUP(IDC_TOOL_COMBO, OnCloseUpToolCombo)
    // 編輯框事件
    ON_EN_CHANGE(IDC_TOOL_EDIT, OnChangeToolEdit)
    ON_EN_SETFOCUS(IDC_TOOL_EDIT, OnSetFocusToolEdit)
    ON_EN_KILLFOCUS(IDC_TOOL_EDIT, OnKillFocusToolEdit)
END_MESSAGE_MAP()

// 組合框選中項改變
void CMainFrame::OnSelChangeToolCombo()
{
    CString str;
    int nSel = m_wndComboBox.GetCurSel();
    if (nSel != CB_ERR)
    {
        m_wndComboBox.GetLBText(nSel, str);
        AfxMessageBox(_T("選中: ") + str);
    }
}

// 組合框編輯內容改變
void CMainFrame::OnEditChangeToolCombo()
{
    CString str;
    m_wndComboBox.GetWindowText(str);
    // 實時過濾/提示邏輯...
    TRACE(_T("Combo 編輯: %s\n"), str);
}

// 編輯框內容改變
void CMainFrame::OnChangeToolEdit()
{
    CString str;
    m_wndEdit.GetWindowText(str);
    TRACE(_T("Edit 變更: %s\n"), str);
}

關鍵技術細節

佔位按鈕索引確定

資源編輯器中工具欄按鈕從 0 開始索引。使用 CToolBar::CommandToIndex(ID) 獲取索引更可靠:


int nComboIndex = m_wndToolBar.CommandToIndex(IDC_TOOL_COMBO);
if (nComboIndex >= 0)
{
    m_wndToolBar.SetButtonInfo(nComboIndex, IDC_TOOL_COMBO, TBBS_SEPARATOR, 160);
    m_wndToolBar.GetItemRect(nComboIndex, &rectCombo);
    // ...
}

控件字體同步

動態創建控件默認使用系統字體,需同步工具欄字體:


CFont* pFont = m_wndToolBar.GetFont();
if (pFont)
{
    m_wndComboBox.SetFont(pFont);
    m_wndEdit.SetFont(pFont);
}

工具欄重新佈局時控件跟隨調整

覆寫 OnSize 或處理 WM_SIZE 重新定位:


void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
    CFrameWnd::OnSize(nType, cx, cy);
    
    // 重新獲取佔位按鈕矩形並移動控件
    CRect rect;
    int nIdx = m_wndToolBar.CommandToIndex(IDC_TOOL_COMBO);
    if (nIdx >= 0)
    {
        m_wndToolBar.GetItemRect(nIdx, &rect);
        rect.bottom += 100;
        m_wndComboBox.MoveWindow(&rect);
    }
    
    nIdx = m_wndToolBar.CommandToIndex(IDC_TOOL_EDIT);
    if (nIdx >= 0)
    {
        m_wndToolBar.GetItemRect(nIdx, &rect);
        m_wndEdit.MoveWindow(&rect);
    }
}

高 DPI / 縮放支持

  • 使用邏輯單位而非硬編碼像素:MulDiv(160, GetDeviceCaps(hDC, LOGPIXELSX), 96)
  • 清單清單啟用 DPI 感知(SetProcessDpiAwareness)
  • MFC 14.0+ (VS2015+) 自動處理大部分縮放

其他標準控件擴展模式

控件類型創建風格示例典型用途
CDateTimeCtrlWS_VISIBLE | WS_TABSTOP | DTS_SHORTDATEFORMAT日期選擇
CSliderCtrlWS_VISIBLE | WS_TABSTOP | TBS_HORZ | TBS_AUTOTICKS縮放/音量/進度
CIPAddressCtrlWS_VISIBLE | WS_TABSTOPIP 地址輸入
CProgressCtrlWS_VISIBLE | PBS_SMOOTH後台任務進度
CButton (下拉)WS_VISIBLE | BS_SPLITBUTTON | BS_ICON功能菜單入口

常見問題排查

現象原因解決
控件不顯示/位置錯誤GetItemRect 獲取矩形錯誤/索引錯誤用 CommandToIndex 確認索引,調試輸出 rect 座標
組合框下拉列表被截斷rect.bottom 未足夠擴展增加 rect.bottom(如 +100~200),或使用 CBS_DROPDOWNLIST 系統自動管理
消息映射不生效ON_CBN_XXX 宏參數 ID 錯誤/未在正確類中ID 必須與 Create 時一致,映射需在控件父窗口類(此處為 CMainFrame)
Tab 鍵無法進入控件未設置 WS_TABSTOP 風格Create 時包含 WS_TABSTOP
工具欄鎖定/浮動時控件錯位未處理重新佈局覆寫 OnSize/RecalcLayout 同步 MoveWindow

總結

MFC 工具欄動態添加標準控件的核心模式是:**資源佔位 → 設置尺寸 → 獲取矩形 → 創建子窗口 → 手動消息映射**。此模式適用於所有標準 Windows 控件(ComboBox、Edit、DateTimePicker、Slider 等),能將原本對話框中的交互操作前移至工具欄,大幅提升界面專業度與操作效率。掌握佔位按鈕索引管理、字體同步、重新佈局跟隨、消息映射手寫等關鍵細節,可構建媲美商業級產品的工具欄界面。

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