Switching between CMFCRibbonBar based Ribbon interface and CMFCMenuBar/CMFCToolBar based interface on-the-fly

Download sample project - MFCRibbonToolBarDemo

Since the advent of MFC Feature Pack, Ribbon bar based interfaces have been extremely popular as well as useful. However, there might be some scenarios/use-cases when one would want to have both the interfaces i.e. the traditional Menu/Toolbar based interface as well as the modern Ribbon bar based interface in one’s legacy SDI/MDI application.

There is a nice article on CodeProject for “How to Support the Ribbon and a Menu in the Same Executable”, but it uses the COM based Windows 7 APIs. As MFC itself now provides classes, such as CMFCRibbonBar, CMFCToolBar, CMFCMenuBar, etc., it has become rather a lot easier; this blog would help achieve this goal (thanks to our Sr. Escalation Engineer, Scot Brennecke, for helping me implement this idea!).

To demonstrate how to switch from ribbon styled interface to menu/toolbar styled interface and vice-versa dynamically at runtime, we’ll have a menu item and/or toolbar button added to "Switch to Ribbon", and a ribbon button to "Switch back to Menu/Toolbar". However, only one of these two controls would be visible at any time.

Now let’s start by creating a very simple MFC SDI based application using the project creation template is Visual Studio 2012 as shown below:

clip_image002[12]

clip_image004[11]

clip_image006[11]

Once this project is created, it would have the default menu/toolbar interface already implemented into it by the VS wizard. We’ll just add a new menu item that would enable us to switch to the ribbon bar, and similarly a ribbon resource with a button into it to switch back to the menu/toolbar. After that, we’ll add the respective event handlers – for example, “OnStyleRibbonInterface” as the menu item control handler and “OnRbnBtnToolbarInterface” as the ribbon button click handler into the main frame class (viz., “CMainFrame”). We also need to add an object of “CMFCRibbonBar” in the CMainFrame to represent the ribbon bar (no need to add any object for menu/toolbar as they would have been already added by the VS wizard itself).

The required code snippet is as below:

 //MainFrm.h : interface of the CMainFrame class

class CMainFrame : public CFrameWndEx
{
    . . .
protected:  //control bar embedded members
    CMFCMenuBar       m_wndMenuBar;
    CMFCToolBar       m_wndToolBar;
    CMFCRibbonBar     m_wndRibbonBar;

public:
    afx_msg void OnStyleRibboninterface();
    afx_msg void OnRbnBtnToolbarInterface();
    . . .
};

//MainFrm.cpp : implementation of the CMainFrame class

/*Menu item control handler to switch to ribbon interface*/
void CMainFrame::OnStyleRibboninterface()
{
    //The ribbon creation is just one time activity.
    //Load/attach the ribbon resource with the ribbon control.
    if(nullptr == m_wndRibbonBar.m_hWnd)
    {
        m_wndRibbonBar.Create(this);
        m_wndRibbonBar.LoadFromResource(IDR_RIBBON1);
    }

    //Show the ribbon and hide the menu/toolbar.
    m_wndRibbonBar.ShowPane(TRUE, TRUE, TRUE);
    m_wndMenuBar.ShowPane(FALSE, FALSE, FALSE);
    m_wndToolBar.ShowPane(FALSE, FALSE, FALSE);

    //Forcefully redraw the frame window without changing its size and position.
    CRect rectThis;
    GetWindowRect(&rectThis);
    SetWindowPos(
        &wndTop, 
        rectThis.left, 
        rectThis.top, 
        rectThis.Width(), 
        rectThis.Height(), 
        SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER
        );
}

/*Ribbon bar button click handler to switch to menu/toolbar interface*/
void CMainFrame::OnRbnBtnToolbarInterface()
{
    //Show the menu/toolbar and hide the ribbon.
    m_wndMenuBar.ShowPane(TRUE, TRUE, TRUE);
    m_wndToolBar.ShowPane(TRUE, TRUE, TRUE);
    m_wndRibbonBar.ShowPane(FALSE, FALSE, FALSE);

    //Forcefully redraw the frame window without changing its size and position.
    CRect rectThis;
    GetWindowRect(&rectThis);
    SetWindowPos(
        &wndTop, 
        rectThis.left, 
        rectThis.top, 
        rectThis.Width(), 
        rectThis.Height(), 
        SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER
        );
}

After adding these lines of code, once the project is built and run, the application would look like below:

clip_image008

clip_image010

Having suggested the above approach, one point to note here is that the legacy styled interface and the modern styled interface don’t fit well together as they aren’t meant to coexist side-by-side. However, if it’s really required, there’s always a way! :)