Bill Heitler Bill Heitler - 1 month ago 5
C++ Question

Example code for CMFCMenuButton?

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.

Answer

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)
    ...
}