CMDIFrameWndEx::EnableMDITabsLastActiveActivation() does not work as expected in MDI Tabbed Style Application

 

Recently I worked with a developer who was developing an MDI Tabbed Style MFC application and wanted to Activate the Last Activated Tab when the current Tab is closed. She was trying to use the method

CMDIFrameWndEx::EnableMDITabsLastActiveActivation() for this purpose, by calling it from the OnCreate() method of her MainFrame class. Her code looked like below

 

intCMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

      if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)

              return -1;

 

       BOOL bNameValid;

      // set the visual manager and style based on persisted value

       OnApplicationLook(theApp.m_nAppLook);

 

       CMDITabInfomdiTabParams;

       mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // other styles available...

       mdiTabParams.m_bActiveTabCloseButton = TRUE; // set to FALSE to place close button at right of tab area

       mdiTabParams.m_bTabIcons = FALSE; // set to TRUE to enable document icons on MDI taba

       mdiTabParams.m_bAutoColor = TRUE; // set to FALSE to disable auto-coloring of MDI tabs

       mdiTabParams.m_bDocumentMenu = TRUE; // enable the document menu at the right edge of the tab area

       EnableMDITabbedGroups(TRUE, mdiTabParams);

 

      EnableMDITabsLastActiveActivation();

 

<snip>

 

 

 

But it was not working as expected. It still activated the last tab in the tab order than the expected‘Last Activated’ tab. Closely analyzing the issue, we found that this unexpected behavior was due to a design flaw in MFC. EnableMDITabsLastActiveActivation() actually changes the the m_wndTab member of the m_wndClientArea member of the CMDIFrameWndEx (base class of CMainFrame).However, when the MDI Tabbed Groups feature is used, it basically hides and disables the m_wndTab and creates new tab controls with CreateTabGroup() and adds to the m_lstTabbedGroups of the m_wndClientArea instead. It effectively nullifies the settings changed by calling EnableMDITabsLastActiveActivation(), as the settings we changed for old Tab control by calling EnableMDITabsLastActiveActivation() is not copied over to the newly created TabControl.

 

In nutshell the issue is happening because the MainFrame Window is not aware of the changes in the settings in its embedded CMDIClientAreaWnd object after the EnableMDITabsLastActiveActivation() call it makes.

 

One may follow below steps to work around this issue

 

 

1. Add a public method, UpdateMDITabGroups to CMainFrame class:

 

void CMainFrame::UpdateMDITabGroups()

{

        for (POSITION pos = m_wndClientArea.GetMDITabGroups().GetHeadPosition (); pos != NULL;)

        {

                        CMFCTabCtrl* pNextTabWnd = DYNAMIC_DOWNCAST(CMFCTabCtrl, m_wndClientArea.GetMDITabGroups().GetNext(pos));

                        ASSERT_VALID(pNextTabWnd);

 

                        pNextTabWnd->EnableActivateLastActive();

        }

}

 

 

2. Call this method from CChildFrame::OnCreate:

int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

        if (CMDIChildWndEx::OnCreate(lpCreateStruct) == -1)

                        return -1;

 

        ((CMainFrame*)AfxGetMainWnd())->UpdateMDITabGroups();

 

        return 0;

}

 

3. And add AFX_WM_ON_MOVETOTABGROUP message handler to CMainFrame:

ON_MESSAGE(AFX_WM_ON_MOVETOTABGROUP, OnMoveToTabGroup)

LRESULT CMainFrame::OnMoveToTabGroup(WPARAM, LPARAM)

{

        LRESULT lRes = Default();

        UpdateMDITabGroups();

 

        return lRes;

}