Sorry for the newbie question, but can anyone point me at sample code that illustrates the use of the CMFCMenuButton? The Microsoft help refers to "New Controls samples", but these samples seem to be in the Visual Studio 2008 "Feature Pack", and this refuses to install on my system since I'm running VS 2013 and don't have VS 2008. I haven't been able to find the samples as stand-alone code. To be specific, I have a dialog bar in which I want a button labelled Save with drop-down options of Save All and Save Visible (with Save All the default). But any working code would at least get me started.
Asked
Active
Viewed 2,817 times
1 Answers
7
Declare data members:
CMFCMenuButton m_button_menu;
CMenu m_menu;
Also add the button's id to message map and data exchange:
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
ON_BN_CLICKED(IDC_MFCMENUBUTTON1, OnButtonMenu)
...
END_MESSAGE_MAP
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_MFCMENUBUTTON1, m_button_menu);
}
Define:
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
//...
m_menu.LoadMenu(IDR_MENU1);
m_button_menu.m_hMenu = m_menu.GetSubMenu(0)->GetSafeHmenu();
return TRUE;
}
Where IDR_MENU1
is a regular menu bar and we get its first submenu. For example:
IDR_MENU1 MENU
BEGIN
POPUP "Dummy"
BEGIN
MENUITEM "&Item1", ID_FILE_ITEM1
MENUITEM "&Item2", ID_FILE_ITEM2
END
END
If button's drop-down arrow is clicked, a popup menu appears, menu result is passed to OnButtonMenu
. If left side of button is clicked, then OnButtonMenu
is called directly, without showing a popup menu.
void CMyDialog::OnButtonMenu()
{
CString str;
switch (m_button_menu.m_nMenuResult)
{
case ID_FILE_ITEM1:
str = L"first menu item clicked";
break;
case ID_FILE_ITEM2:
str = L"second menu item clicked";
break;
default:
str = L"Button click (popup menu did not appear, or menu ID is not handled)";
break;
}
MessageBox(str);
}
** When working with docking controls, dialog bars, etc. MFC may run its own subclass, I don't think DoDataExchange
gets called. m_button_menu
could be invalid. GetDlgItem
can be used to find the correct pointer:
CMFCMenuButton* CMyDlgBar::GetButtonMenu()
{
CMFCMenuButton* pButton = &m_button_menu;
if (!IsWindow(pButton->m_hWnd))
pButton = (CMFCMenuButton*)GetDlgItem(IDC_MFCMENUBUTTON1);
return pButton;
}
Everywhere else we use GetButtonMenu()
instead of m_button_menu
. For example:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
//...
m_dlgbar.Create(...);
m_dlgbar.m_menu.LoadMenu(IDR_MENU1);
m_dlgbar.GetButtonMenu()->m_hMenu = m_dlgbar.m_menu.GetSubMenu(0)->GetSafeHmenu();
return 0;
}
void CMainFrame::OnButtonMenu()
{
CString str;
switch (GetButtonMenu()->m_nMenuResult)
...
}
What if the Drop-Down Arrow does not show?
Then read the answer here that explains the changes needed to your RC file.

Andrew Truckle
- 17,769
- 16
- 66
- 164

Barmak Shemirani
- 30,904
- 6
- 40
- 77
-
To make this sample complete you could add the `IDR_MENU1` resource script. – IInspectable Aug 13 '15 at 22:17
-
Update: added sample menu resource as suggested – Barmak Shemirani Aug 13 '15 at 23:29
-
Many thanks for this quick and very clear explanation. However, I can't seem to get it to work in my CDialogBar -derived class. I have tried the DDX_ route as above, and I have also tried just sub-classing my CMFCMenuButton member variable in OnInitDialog (having done the mods needed for that function to work in a dialog bar), but in both cases I get a catastrophic ASSERT cascade culminating in – Bill Heitler Aug 14 '15 at 20:24
-
sorry - ran out of editing time before I finished with: BOOL CWnd::Attach(HWND hWndNew) { ASSERT(m_hWnd == NULL); // only attach once, detach on destroy ASSERT(FromHandlePermanent(hWndNew) == NULL); // must not already be in permanent map – Bill Heitler Aug 14 '15 at 20:32
-
It looks like `DoDataExchange` is not called in `CDialogBar` and `SubclassDlgItem` fails because MFC is doing its own thing. Call `IsWindow(m_button_menu.m_hWnd)` to make sure the control is initialized, or use `GetDlgItem`. See updated answer for `CDialogBar` – Barmak Shemirani Aug 15 '15 at 02:40
-
Perfection on a stick! It's working. I really don't think I would have got there on my own by experiment, so many thanks again. [Just for anyone copying - I think I needed m_dlgbar.GetButton ... in the Frame handler.] – Bill Heitler Aug 15 '15 at 10:43
-
I usually override the CMFCMenuButton::OnShowMenu method to process clicking its options. – sergiol Aug 17 '15 at 17:39
-
NOTE: I had big problems initialising the CMFCMenuButton because I was using the "MFC MenuButton Control" from the Toolbox in the VS2010 GUI. But using a straight "Button" control made it work fine. Not sure why... – David Coster Jan 30 '17 at 04:39