MFC 默認工具欄(CToolBar)僅支持按鈕。專業應用(如 Visual Studio、Office)工具欄常包含組合框、編輯框、下拉按鈕等標準控件。本文介紹在工具欄預留佔位按鈕 → 動態創建子控件 → 消息映射響應的通用實現模式。
IDC_TOOL_COMBO),作為佔位符CToolBar::SetButtonInfo 設置佔位按鈕寬度、風格(TBBS_SEPARATOR)CToolBar::GetItemRect 獲取佔位按鈕矩形區域Create 在工具欄內創建子窗口ON_CBN_SELCHANGE 等)
// MainFrm.h
class CMainFrame : public CFrameWnd
{
// ...
protected:
CToolBar m_wndToolBar;
CComboBox m_wndComboBox; // 工具欄組合框
CEdit m_wndEdit; // 工具欄編輯框
// ...
};
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;
}
因動態創建控件,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);
}
}
MulDiv(160, GetDeviceCaps(hDC, LOGPIXELSX), 96)SetProcessDpiAwareness)| 控件類型 | 創建風格示例 | 典型用途 |
|---|---|---|
CDateTimeCtrl | WS_VISIBLE | WS_TABSTOP | DTS_SHORTDATEFORMAT | 日期選擇 |
CSliderCtrl | WS_VISIBLE | WS_TABSTOP | TBS_HORZ | TBS_AUTOTICKS | 縮放/音量/進度 |
CIPAddressCtrl | WS_VISIBLE | WS_TABSTOP | IP 地址輸入 |
CProgressCtrl | WS_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 等),能將原本對話框中的交互操作前移至工具欄,大幅提升界面專業度與操作效率。掌握佔位按鈕索引管理、字體同步、重新佈局跟隨、消息映射手寫等關鍵細節,可構建媲美商業級產品的工具欄界面。