本講涵蓋選單與工具列的視覺化設計、命令訊息對應 (COMMAND 與 UPDATE_COMMAND_UI)、工具按鈕與選單命令的聯動、工具列顯示/隱藏的多種控制方式(選單命令、快速鍵、捷徑選單),以及狀態列窗格的動態文字顯示技巧。
& 標示,如「格式(&M)」。Menu 下的 IDR_MAINFRAME。ID_FOMAT_TXTFONTID_FORMAT_TXTCOLOR
圖 1 頂層選單資源編輯

圖 2 選單項屬性設定(Caption、Popup、Separator、Prompt)

圖 3 加入完成的「格式」選單
執行程式時新選單項呈現灰色無法選取,因尚未對應命令訊息。對應步驟:
CViewerView,屬性視窗點選 [事件] 按鈕。ID_FORMAT_TXTFONT,展開其 [+] 號,於 COMMAND 欄位選擇 <新增> OnFormatTxtfont。ID_FORMAT_TXTCOLOR 加入 COMMAND 對應。
圖 4 事件對應介面

圖 5 選單 COMMAND 訊息對應
注意:單一檔案應用程式中,命令訊息回應優先順序為:視圖類別 > 文件類別 > 主框架視窗類別 > 應用程式類別。
CViewerView 加入兩個成員變數:
LOGFONT m_lfTextFont — 邏輯字型結構,描述理想字型特性COLORREF m_crTxtColor — RGB 顏色值m_lfTextFont 設為預設字型,m_crTxtColor = RGB(0,0,0)(黑色)。OnFormatTxtfont 使用 CFontDialog 通用字型對話框:void CViewerView::OnFormatTxtfont()
{
CFontDialog dlg(&m_lfTextFont);
if (dlg.DoModal() == IDOK)
{
dlg.GetCurrentFont(&m_lfTextFont);
Invalidate(); // 觸發重繪
}
}

圖 6 字型選擇代碼
OnFormatTxtcolor 使用 CColorDialog 通用顏色對話框:void CViewerView::OnFormatTxtcolor()
{
CColorDialog dlg(m_crTxtColor);
if (dlg.DoModal() == IDOK)
{
m_crTxtColor = dlg.GetColor();
Invalidate();
}
}

圖 7 顏色選擇代碼
OnDraw 套用字型與顏色繪製文字。
圖 8 修改後的 OnDraw 繪製代碼

圖 9 程式執行結果:字型與顏色變更生效
Toolbar 選 [插入 Toolbar],預設 ID 為 IDR_TOOLBAR1。
圖 10 工具按鈕設計介面
ID_FORMAT_TXTFONT,Prompt 設為「改變顯示的字型\n字型」ID_FORMAT_TXTCOLOR,Prompt 設為「改變文字的顯示顏色\n顏色」Prompt 屬性說明:\n 前文字顯示於狀態列,\n 後文字作為工具提示彈出視窗內容。
CMainFrame 加入成員變數 CToolBar m_wndFormatBar。CMainFrame::OnCreate 中建立工具列:if (!m_wndFormatBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndFormatBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("無法建立工具列\n");
return -1;
}
m_wndFormatBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndFormatBar);
// 避免與預設工具列 ID 衝突
m_wndFormatBar.SetWindowLong(GWL_ID, AFX_IDW_TOOLBAR + 10);

圖 11 工具列建立代碼
關鍵點說明:
CreateEx 建立工具列物件,LoadToolBar 載入資源。EnableDocking / DockControlBar 實現停靠/浮動功能。

圖 12 新工具列的停靠與浮動狀態
因工具按鈕 ID 與選單命令相同,無需額外對應 COMMAND 訊息;若為獨立按鈕則需自行對應。
在 [檢視] 選單加入「格式工具列(&F)」,ID 為 ID_VIEW_FORMAT。

圖 13 [檢視] 選單加入工具列切換項目
於 CMainFrame 加入 BOOL m_bViewFormat 追蹤顯示狀態,建構函數初始化為 TRUE。對應 ID_VIEW_FORMAT 的 COMMAND 與 UPDATE_COMMAND_UI:
void CMainFrame::OnViewFormat()
{
m_bViewFormat = !m_bViewFormat;
ShowControlBar(&m_wndFormatBar, m_bViewFormat, FALSE);
}
void CMainFrame::OnUpdateViewFormat(CCmdUI* pCmdUI)
{
pCmdUI->Enable();
pCmdUI->SetCheck(m_bViewFormat);
}

圖 14 工具列顯示控制代碼
CCmdUI::Enable() 控制啟用/停用,SetCheck() 控制選單項前的核取記號。
Accelerator,雙擊 IDR_MAINFRAME。ID_VIEW_FORMAT,修飾鍵 Ctrl,鍵 1,類型 VIRTKEY。
圖 15 快速鍵資源編輯器

圖 16 新增預設快速鍵項目

圖 17 設定 Ctrl+1 快速鍵
執行後按 Ctrl+1 即可切換工具列。建議於選單項文字後加上 \t Ctrl+1 讓使用者知悉快速鍵。
最常用的工具列切換方式。實作步驟:
CMainFrame 屬性視窗 [訊息] 頁面加入 WM_CONTEXTMENU 對應。OnContextMenu 撰寫代碼:void CMainFrame::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (pWnd->GetDlgCtrlID() >= AFX_IDW_DOCKBAR_TOP &&
pWnd->GetDlgCtrlID() <= AFX_IDW_DOCKBAR_FLOAT)
{
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
CMenu* pPopup = menu.GetSubMenu(1); // 取得第 2 個子選單 (檢視)
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
}
}

圖 18 加入 WM_CONTEXTMENU 訊息對應

圖 19 OnContextMenu 實作代碼
說明:AFX_IDW_DOCKBAR_TOP~AFX_IDW_DOCKBAR_FLOAT 為工具列停靠/浮動視窗 ID 範圍。GetSubMenu(1) 取得第 2 個子選單 (索引從 0 起)。TrackPopupMenu 於滑鼠右鍵位置彈出選單。


圖 20 不同位置右鍵彈出的捷徑選單
ID_STAT_TXTFONT (值 101)ID_STAT_TXTCOLOR (值 102)
圖 21 新增資源符號 ID

圖 22 字串表加入窗格文字
MainFrm.cpp 中 indicators 陣列,加入兩個新 ID:static UINT BASED_CODE indicators[] =
{
ID_SEPARATOR, // 狀態列訊息區
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
ID_STAT_TXTFONT, // 新增:字型顯示窗格
ID_STAT_TXTCOLOR // 新增:顏色顯示窗格
};

圖 23 狀態列窗格陣列修改

圖 24 初始執行結果(僅顯示靜態字串)
因類別屬性視窗無法直接對窗格 ID 對應事件,採用臨時選單法:
ID_STAT_TXTFONT、ID_STAT_TXTCOLOR。CViewerView 對應兩個 ID 的 UPDATE_COMMAND_UI 事件。void CViewerView::OnUpdateStatTxtfont(CCmdUI* pCmdUI)
{
pCmdUI->Enable();
CString str;
str.Format(_T("字型: %s"), m_lfTextFont.lfFaceName);
pCmdUI->SetText(str);
}
void CViewerView::OnUpdateStatTxtcolor(CCmdUI* pCmdUI)
{
pCmdUI->Enable();
CString str;
str.Format(_T("顏色: RGB(%d,%d,%d)"),
GetRValue(m_crTxtColor), GetGValue(m_crTxtColor), GetBValue(m_crTxtColor));
pCmdUI->SetText(str);
}

圖 22 狀態列窗格動態更新代碼

圖 23 最終執行結果:狀態列即時顯示目前字型與顏色
| 問題 | 解決方法 |
|---|---|
| 新增選單/工具按鈕 ID 數值固定為 0 | 屬性視窗中 ID 名稱後加 =200 按 Enter,或指定其他唯一值 |
| 事件對應頁面找不到新增的命令 ID,或顯示為數值 | 以「原始碼 (文字) 編輯器」開啟 .rc 檔,將數值改回符號名稱 |
| 編譯出現「無法開啟 xxx 資源」錯誤 | 執行 [重新建置方案] |
| 執行時出現 LNK1168 無法寫入 .exe | 關閉正在執行的程式後再編譯執行 |

圖 24 以原始碼編輯器開啟 .rc 檔

圖 25 .rc 檔選單文字片段
本講詳細說明選單、工具列、狀態列三大介面元素的設計、訊息對應、聯動機制與動態更新技巧。下一講將進入對話框介面設計、模態/非模態對話框建立,以及 DDX/DDV 資料交換驗證機制的應用。