建立一個按鈕控制項,點擊時彈出選單供使用者進一步選擇 (如工具列「新增」分割按鈕、瀏覽器「回上一頁」下拉歷史)。支援:文字/箭頭顯示、多種箭頭方向、選單彈出位置、焦點矩形控制、命令 ID 返回。
BS_OWNERDRAW,MFC 呼叫 CButton::DrawItem 繪製外觀。BN_CLICKED 處理中取得按鈕螢幕座標,呼叫 CMenu::TrackPopupMenu 顯示選單,TPM_RETURNCMD 旗標使其返回使用者選擇的命令 ID。CMenuButton 類別宣告
class CMenuButton : public CButton {
public:
enum ArrowType { ARROW_DOWN, ARROW_RIGHT, ARROW_DOWN_SMALL, ARROW_RIGHT_SMALL, ARROW_UP, ARROW_LEFT };
enum PopupPos { POPUP_TOPLEFT, POPUP_TOPRIGHT, POPUP_BOTTOMLEFT, POPUP_BOTTOMRIGHT };
CMenuButton();
void SetArrowType(ArrowType type); // 預設 ARROW_DOWN
void SetMenuPopupPos(PopupPos pos); // 預設 POPUP_BOTTOMLEFT
void SetDrawFocusRect(BOOL bDraw); // 預設 TRUE
void SetStyle(BOOL bDrawArrow); // FALSE 僅顯示文字
UINT OnClick(UINT nMenuID, UINT nSubMenu = 0); // 外部呼叫處理點擊
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
virtual void PreSubclassWindow();
afx_msg void OnClicked(); // BN_CLICKED 處理
ArrowType m_arrowType;
PopupPos m_popupPos;
BOOL m_bDrawFocusRect;
BOOL m_bDrawArrow;
DECLARE_MESSAGE_MAP()
};
PreSubclassWindow 確保 BS_OWNERDRAW
void CMenuButton::PreSubclassWindow() {
ModifyStyle(0, BS_OWNERDRAW); // 強制自繪
CButton::PreSubclassWindow();
}
DrawItem 繪製按鈕外觀與箭頭
void CMenuButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
CRect rect = lpDIS->rcItem;
UINT state = lpDIS->itemState;
// 1. 繪製按鈕框架 (凸起/按下/焦點)
UINT uEdge = (state & ODS_SELECTED) ? EDGE_SUNKEN : EDGE_RAISED;
pDC->DrawEdge(&rect, uEdge, BF_RECT | BF_ADJUST | (m_bDrawFocusRect && (state & ODS_FOCUS) ? BF_FOCUS : 0));
// 2. 繪製文字 (若不畫箭頭)
if (!m_bDrawArrow) {
CString strText; GetWindowText(strText);
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(strText, &rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
return;
}
// 3. 繪製 Marlett 箭頭 (置中或偏右)
CFont fontMarlett;
fontMarlett.CreateFont(rect.Height()*2/3, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett"));
CFont* pOldFont = pDC->SelectObject(&fontMarlett);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
TCHAR chArrow = _T('4'); // 預設向下
switch (m_arrowType) {
case ARROW_RIGHT: chArrow = _T('5'); break;
case ARROW_DOWN_SMALL: chArrow = _T('a'); break; // 小箭頭字元 (需查表)
case ARROW_UP: chArrow = _T('6'); break;
case ARROW_LEFT: chArrow = _T('7'); break;
}
CSize sz = pDC->GetTextExtent(&chArrow, 1);
CPoint pt((rect.left + rect.right - sz.cx)/2, (rect.top + rect.bottom - sz.cy)/2);
// 依 m_popupPos 微調位置...
pDC->TextOut(pt.x, pt.y, &chArrow, 1);
pDC->SelectObject(pOldFont);
}
OnClick 彈出選單並返回命令 ID
UINT CMenuButton::OnClick(UINT nMenuID, UINT nSubMenu) {
CMenu menu;
if (!menu.LoadMenu(nMenuID)) return 0;
CMenu* pPopup = menu.GetSubMenu(nSubMenu);
if (!pPopup) return 0;
CRect rect; GetWindowRect(&rect);
CPoint pt;
switch (m_popupPos) {
case POPUP_BOTTOMLEFT: pt = rect.BottomLeft(); break;
case POPUP_BOTTOMRIGHT: pt = rect.BottomRight(); break;
case POPUP_TOPLEFT: pt = rect.TopLeft(); break;
case POPUP_TOPRIGHT: pt = rect.TopRight(); break;
}
// TPM_RETURNCMD: 返回選擇的命令 ID;TPM_LEFTALIGN/TPM_RIGHTALIGN 對齊
UINT nCmd = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
pt.x, pt.y, GetParent(), NULL);
return nCmd; // 0 表示取消/無選擇
}
// 對話框類別成員
CMenuButton m_btnMenu;
// OnInitDialog
m_btnMenu.SubclassDlgItem(IDC_MENUBUTTON, this);
m_btnMenu.SetArrowType(CMenuButton::ARROW_DOWN);
m_btnMenu.SetMenuPopupPos(CMenuButton::POPUP_BOTTOMLEFT);
m_btnMenu.SetWindowText(_T("選項"));
// BN_CLICKED 處理 (類別精靈添加)
void CMyDialog::OnClickedMenubutton() {
UINT nCmd = m_btnMenu.OnClick(IDR_MENU_BUTTON, 0);
if (nCmd) {
// 處理命令,或轉發
SendMessage(WM_COMMAND, nCmd);
}
}
| 字元 | 符號 | 用途 |
|---|---|---|
| '0' (0x30) | □ | 最大化 |
| '1' (0x31) | — | 最小化 |
| '2' (0x32) | ⧉ | 還原 |
| '3' (0x33) | ✕ | 關閉 |
| '4' (0x34) | ▼ | 向下箭頭 (下拉) |
| '5' (0x35) | ▶ | 向右箭頭 (子選單) |
| '6' (0x36) | ▲ | 向上箭頭 |
| '7' (0x37) | ◀ | 向左箭頭 |