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;
}