Automatic Tab Bar for MDI programs

Environment VC++ V5.0; Win98 (NT should work too)

Yet another tabbed view thing? Yes and no. This one is different. No changes to the DOC, VIEW or CHILDFRAME classes are neccessary.

The TabBar is a child window of CMainFrame and can maintain the MDI child frames containing the various views.

We subclasses the MDI-CLIENT window to get the required information automatically. No matter which view classes you are using, it's tab appears automatically. The Frames title is used as the label for the tabs.

I tried to mimic the behaviour of my favorite editor "Multi-Edit V8". One major design goal was to find a "plug in" solution. Easy to use.

How to use it:

1.Add these files to your project.

MdiClient.cpp

MTabWnd.cpp

TabBar.cpp

MdiClient.h

MTabWnd.h

TabBar.h

app.h

TabCtrlEx.cpp (Written by Chris Maunder)

TabCtrlEx.h (Written by Chris Maunder)

2. Change app.h to contain an #include "YourAPP.h" .
3. Derive your CMainFrame window from CMDITabFrameWnd.
4. Call the CreateTabs function at the end of CMainFrame::OnCreate.

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	......
	if (CMDITabFrameWnd::CreateTabs() == -1)
		return -1;

	return 0;
 }

5.With "View/Resource Symbols" add two unique ID's to resource.h.
#define IDC_MDI_TAB_CTRL_BAR ???

#define IDC_MDI_TAB_CTRL ???

6.With the resource editor add two menu entries to the windows menus.
Both are not required for the tabs, but I added this functionality to CMDITabFrameWnd .

MENUITEM "Ne&xt MDI Window", ID_WINDOW_NEXT_PANE

MENUITEM "Pre&vious MDI Window", ID_WINDOW_PREV_PANE

7.Done!
Good luck.

Unicode

I did not test to compile with Unicode, but it should work.

To Do

Adding icons to the tabs.
Could be either automatic icons from the resources or a list of icons. Any ideas?
Adding an option to hide the tabs if the MDI childs are not maximized.
I tried to switch from tabs to buttons in non-maximized mode, but failed. The style change seams to be ignored by the TabCntrl. Any ideas?
Tooltips!
In case of a file name it can show the complete path.

Acknowledgements

Ivan Zhakovs "Windows Manager" gave me some usefull ideas.

Chris Maunder wrote the CTabCntrlEx class for a better look and feel of the tabs.

Many articles here in codeguru.com.

"MFC Internals" from George Shepherd and Scot Wingo.

"The MFC Answer Book" from Eugene Kain.

Code Snippets

Below are a few snippets out of the source code. Just the highlights are shown to give you an idea how it works. Download the sources for details.

The creation of the TabBar and subclassing of the MDI-CLIENT window:


BOOL CMDITabFrameWnd::CreateTabs(void)
{
	if (!m_wndTabs.Create(this,WS_VISIBLE|WS_CHILD|CBRS_TOP|WS_EX_WINDOWEDGE, IDC_MDI_TAB_CTRL_BAR)  )
	{
		TRACE("Failed to create test status bar\n");
		return -1;      // fail to create
	}

	// The MDI Client needs to talk to the TabBar
	m_wndMdiClient.m_pWndTabs = &m_wndTabs;
	ASSERT(m_hWndMDIClient != NULL);

	// This is to get notifications for adding/relemoving tabs (automatically)
	if (!m_wndMdiClient.SubclassWindow(m_hWndMDIClient)  )
	{
		TRACE("Failed to subclass MDI client\n");
		return -1;      // fail to create
	}
	return 0;
}

This is how we get the the window handles of the MDI frames.

The CMdiClient class does subclass the MDI-CLIENT window and therefore has message handlers for creating and destroying the MDI child frames. Only the cration path is show here, because the deletion path works pretty similar.


/////////////////////////////////////////////////////////////////////////////
// CMdiClient message handlers

LRESULT CMdiClient::OnMDICreate(WPARAM wParam, LPARAM lParam)
{
	HWND hWnd = (HWND) DefWindowProc(WM_MDICREATE,  wParam, lParam);
	AddHandle(hWnd);
	return (LRESULT) hWnd;
}

/////////////////////////////////////////////////////////////////////////////
void CMdiClient::AddHandle(HWND hWnd)
{
	ASSERT(m_pWndTabs != NULL);
	m_pWndTabs->AddHandle(hWnd);
}

m_pWndTabs points to an instance of CTabBar.



/////////////////////////////////////////////////////////////////////////////
// Add a new window handle to the tab control
// Also creates a new tab
void CTabBar::AddHandle(HWND hWnd)
{
	CWnd *pFrame = FromHandle(hWnd);
	ASSERT(pFrame != NULL);
	if ( pFrame->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)) )
	{
		TC_ITEM tci;
		tci.mask = TCIF_PARAM|TCIF_TEXT;
		tci.pszText = "";
		tci.lParam = (long) hWnd;
		m_tabctrl.InsertItem(m_tabctrl.GetItemCount(), &tci);
	}
}


Automatic update the tab labels:


// This is called in idle time.
// Here we update the tab labels and selection
void CTabBar::SetTitles(void)
{
	CString csName;
	TC_ITEM tci;
	char buf[MAX_PATH+1];
	CMDIFrameWnd *pMainframe = ((CMDIFrameWnd *)AfxGetMainWnd());
	if (pMainframe == NULL)
		return;	// no official mainframe window yet

	CMDIChildWnd* pActive = pMainframe->MDIGetActive( NULL );

	// update all tab labels if neccessary
	int numitems = m_tabctrl.GetItemCount();
	for (int item = 0; item < numitems; item++)
	{
		csName.Empty();
		tci.mask = TCIF_PARAM|TCIF_TEXT;
		tci.pszText = buf;
		tci.cchTextMax = MAX_PATH;
		if (!m_tabctrl.GetItem(item, &tci))
			continue;	// skip bad ones (never saw one yet)

		CWnd *pFrame = FromHandle((HWND) tci.lParam);
		ASSERT(pFrame != NULL);
		if ( pFrame->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)) )
		{
			pFrame->GetWindowText(csName);
			if (buf != csName)	// avoid flicker
			{
				tci.mask = TCIF_TEXT;
				tci.pszText = csName.LockBuffer();
				m_tabctrl.SetItem(item, &tci);
				csName.UnlockBuffer();
			}
			if (pActive == pFrame)	// mark the active one
				m_tabctrl.SetCurSel(item);
		}
	}
}

Select MDI child frame:


// The user clicked onto a tab.
// Now select a new MDI child frame
void CTabBar::OnSelchange(NMHDR* pNMHDR, LRESULT* pResult)
{
	int idx = m_tabctrl.GetCurSel();

	TC_ITEM tci;
	tci.mask = TCIF_PARAM;
	if (m_tabctrl.GetItem(idx, &tci))
	{
		CWnd *pFrame = FromHandle((HWND) tci.lParam);
		ASSERT(pFrame != NULL);
		ASSERT_KINDOF(CMDIChildWnd, pFrame);
		((CMDIFrameWnd *)AfxGetMainWnd())->MDIActivate(pFrame);
	}
	*pResult = 0;
}

Files of interrest:

Source files:

MdiClient.cpp Subclasses the MDI-CLIENT

MTabWnd.cpp Derive your mainframe from this class

TabBar.cpp The TabBar with auto update of the tab labels

Header Files:

MdiClient.h

MTabWnd.h

TabBar.h

app.h redirects to Mditab.h

Other contributions:

The following files contain a class to improve the look and feel of the tabs. Downloaded from codegure.com and used unchanged in this demo. Written by Chris Maunder. See "Implementing an owner drawn Tab Control" in codeguru.com. The classes above also work without using this nice pice of code.

TabCtrlEx.cpp

TabCtrlEx.h

Generated by AppWizard and changed:

mainfrm.cpp

mainfrm.h

resource.h Updated by the resource editor: Two entries for unique ID's, two menu ID's. App, View and doc classes need no change at all! The files TabCtrlEx.cpp and EditView.cpp are generating compiler warning with level 4. The warnings seam ok to me and I did not change the code on purpose. Both files are not writen by myself and I did not want to change them just because of this, I only use them to demonstrate my code. EditView.cpp is even generated by AppWizard!

Download demo project - 45 KB

Download source - 11 KB

Date Last Updated: May 14, 1999



Comments

  • poppy plan suitable clarisonic pro is thoroughly famous in australia

    Posted by iouwanzi on 06/07/2013 01:44am

    [url=http://www.miaclarisonicaustralia.org/]clarisonic australia[/url] Les producteurs primaires suivantes commencé pour sa bonne qualité merveilleuse de votre sèche-cheveux et fer à lisser est aujourd’hui célèbre également la distinction entre les femmes qui parfois vous avez besoin pour redresser votre chevelure afin d’éliminer ces types de problèmes ghd fer luxe Violet, à son tour, n’est pas un peu ne serait tout simplement pas seulement en possession du logiciel. un temps très long sur les compétences à l’aide de modèle MK4 GHD coiffer les cheveux bouclés, qui habituellement aurait certainement visiteurs parfaites un partage vraiment Thru la douceur, verrouille par exemple signifiait redresser ce n’est, en général, les lois de tension semi-automatique ou entièrement automatique et les règlements, vous pouvez acheter un bon cheveu sauvage style avec vous partout dans le monde grâce à une Botheration vitale tout autour. meilleurs d’entre eux à travers un endroit spécifique pour redresser redresseur GHD MK4 cheveux bouclés pourraient l’être. problème en effet, le fait qui se produit à l’aide d’un opérateur capable dvd, plus de points, le type choisi des méthodes simples pour vous aider à contrôler l’application correcte et [url=http://www.australiaclarisonic.com/]cheap clarisonic australia[/url] Avec concernant la saison de Noël, j’ai couru à travers une idée de cadeau vraiment attractif, votre package de Collection de nuit. un nouvel ensemble de boîte très exquis option limitée en place qui comprend également tous les flambant neuf G Platinum intemporelle GHD Styler haute définition, matériel résistant à la chaleur, seulement deux caractéristiques montre les cheveux bouclés, un nouveau miroir de belle main baroque, un séchoir de cheveux bouclés nouvelles d’acquérir voyage autour ultra-compact, ainsi qu’une belle organisation. [url=http://www.miaclarisonicaustralia.org/]clarisonic mia[/url] Mon conjoint et moi posséder prolongée (pratique pour vous aider mes coudes personnels) mince, okay, colorés mais aussi soulignée, nui chevelure. Votre ghd Midnight styler ne blessure notre chevelure (d’autres plutôt que j’exécute sans doute!)#) en quelque sorte. Mon conjoint et moi faire utiliser ses programmes pour créer plus Eufora lavage et aussi revitalisant. Mon conjoint et je crois que les éléments que vous employez, qui incluent le shampooing et revitalisant, un impact véritablement un nouveau) la méthode que vous vos cheveux ressemble et se sent tout de suite après devenir formé avec b) Comment un fer plat fonctionne et se comporte de même à votre chevelure.

    Reply
  • Lightweight smart – Nike Loose TR Fit in shoot up 2013 3 series

    Posted by Tufffruntee on 04/20/2013 03:49am

    Nike Free TR Suited 3 noteworthy features is to exploit the new scheme: Nike On the loose 5 soles improved bending Groove; stylish tractor imitate making training more focused when; lighter ballast, the permeability is stronger, and more fashionable shoe designs not lone order shoes [url=http://northernroofing.co.uk/roofins.cfm]nike free run uk[/url] more smug wearing, barefoot training sensible of, but also more fashionable appearance. Nike Manumitted TR Then 3 provides first-class lateral stability, you can have the legs in the leg during training. Strong vamp upper breathable mesh, lower soap up's one of a kind design can be [url=http://fossilsdirect.co.uk/glossarey.cfm]nike huarache[/url] seen through it. Lightweight, difficult, piddling soap up means used past completely occasional seams, more flexible, help is stronger. Demand more help, department of a training exercise, bubbles neck in more parts of the neediness championing give, bubbles loose. Use twice talk moisture wicking mock materials, tiresome on your feet, mitigate maintenance feet sear and comfortable. Phylite [url=http://turbo-vac.co.uk/components_13.cfm]nike free womens[/url] midsole offers lightweight surprise level, famous durability and sedate outsole can do to greatly lower the overall dialect heft of the shoe. Qianzhang pods on the outsole and heel-shaped Na媣e rubber enhances the shoe multi-directional drag on sundry surfaces.

    Reply
  • why the screen always flashes when we switch the Tab?

    Posted by Legacy on 11/29/2001 12:00am

    Originally posted by: pear Zhang

    why the screen always flashes when we switch the Tab?

    Reply
  • How to add a tab item manually?

    Posted by Legacy on 06/12/2001 12:00am

    Originally posted by: Martin Unsin

    How can I add a tab item manually?
    
    

    I tried the following:

    void CMDITabFrameWnd::OnFensterNeu()
    {
    MDICREATESTRUCT mdic;
    char docClass[] = "MDICHILD";

    mdic.szClass = docClass;
    mdic.szTitle = "Untitled";
    mdic.hOwner = this;
    mdic.x = mdic.y = mdic.cx = mdic.cy = CW_USEDEFAULT;
    mdic.style = 0;

    ::SendMessage(m_hWndMDIClient, WM_MDICREATE, 0,(LPARAM)&mdic);


    }

    but it doesn't work!

    Can anyone help me?


    Thanks, Martin


    Reply
  • Demo Download not working properly

    Posted by Legacy on 04/19/2001 12:00am

    Originally posted by: Praveen Rao

    We downloaded ur demo , but it's not working properly.
    Can u please tell us clearly , how we can do these tabbed views.

    Reply
  • The case of the disappearing tabs...

    Posted by Legacy on 04/02/2000 12:00am

    Originally posted by: David Bates

    Hi,
    VC6 SP3, Windows NT SP5

    Seems that if you create a lot of tabs quickly in normal sized windows, then maximise a child, then start closing them suddenly they seem to disappear. You need to randomly click the tab bar to find your tabs as the background tabs fail to be redrawn.

    This only seems to happen if you make enough tabs to make the scroll arrows appear on the top right hand corner and if you make them all in normal_size then maximise the last one before starting to close them.

    Might seem trivial however it is not so trivial if your application creates a lot of files at once eg. 20 charts within around 3-4 secs. A user then maximises the window to view the charts. Closing them produces the disappearing tabs...

    Cheers

    David Bates

    Reply
  • Thanks and Integration with Kirk Stowell's DevStudio-like sample

    Posted by Legacy on 07/21/1999 12:00am

    Originally posted by: Scott Smith

    Thanks for such a useful and easy to use enhancement. I will take a look at the print preview problem if I get a chance.

    To use this class with Kirk Stowell's DevStudio-like sample, just edit the MTabWnd.h and MTabWnd.cpp files so that CMDITabFrameWnd is derived from Kirk's CCJMDIFrameWnd class instead of CMDIFrameWnd. Works great.

    Reply
  • Toggling the tab bar on and off

    Posted by Legacy on 06/02/1999 12:00am

    Originally posted by: Rick York

    Thanks for a useful code contribution.
    
    I successfully added the tabs to the enhanced
    CrystalEdit app I have been working on lately.
    One handy capability I did not see described anywhere
    is to toggle the tab bar on and off. I was able to
    figure out how by examining the MFC source code and
    it's actually quite easy. Here's how :

    In resource.h add this definition :

    #define ID_VIEW_TABBAR 32905

    The value doesn't matter much as long as it is unique.

    In the resource script, add the following menu item
    for the IDR_MDITABTYPE and IDR_MAINFRAME menus and
    under the View popup :

    MENUITEM "&Tab Bar", ID_VIEW_TABBAR

    Also, add the following entry to the string table

    ID_VIEW_TABBAR "Show or hide the tab bar\nToggle TabBar"

    In MainFrm.h add these member prototypes to CMainFrame :

    afx_msg BOOL OnTabBarToggle();
    afx_msg void OnUpdateTabBarMenu( CCmdUI* pCmdUI );

    Lastly, in MainFrm.cpp :

    in the CMainFrame message map add these two -

    ON_UPDATE_COMMAND_UI(ID_VIEW_TABBAR, OnUpdateTabBarMenu)
    ON_COMMAND(ID_VIEW_TABBAR, OnTabBarToggle)

    and these two member functions -

    void CMainFrame::OnUpdateTabBarMenu(CCmdUI* pCmdUI)
    {
    CTabBar* pBar = &m_wndTabs;
    pCmdUI->SetCheck((pBar->GetStyle() & WS_VISIBLE) != 0);
    }


    BOOL CMainFrame::OnTabBarToggle()
    {
    CTabBar* pBar = &m_wndTabs;
    ShowControlBar( pBar,
    (pBar->GetStyle() & WS_VISIBLE) == 0,
    FALSE);
    return TRUE;
    }

    This will let you toggle the tab bar just like you would
    the status bar or tool bar. It would be a fairly simple
    matter to extend this so that the state of the tab bar is
    saved to and restored from and ini file or the registry.


    Reply
  • Try this -

    Posted by Legacy on 05/19/1999 12:00am

    Originally posted by: Albert Gao

    1) Use File->new 2 or 3 times to create several child frame windows.
    2) Maximize the child frame window
    3) File->Print Preview
    4) Switch tabs
    5) Close preview window

    Can you find something strange at the corner of the window?

    Albert

    • RE: RE: Try this - (Print Preview Problem - FIXED)

      Posted by kaosd on 05/22/2007 06:35am

      Oops... my apologies for the poor formatting on the previous post... it was my first post here :o) Cheers, kaosd

      Reply
    • RE: Try this - (Print Preview Problem - FIXED)

      Posted by kaosd on 05/22/2007 06:31am

      Hi... I know it's kinda late but this is an excellent article from Dieter and I'm sure people are still stumbling over it and putting it to good use!
      To fix the corrupted system menu in the Print Preview, I changed the behavior so that when tabs are switched, Print Preview is closed and the user is taken back to the tab s/he clicked on. Here's the code:
      //In TabBar.cpp: void CTabBar::OnSelchange()
      {
      //...
      ASSERT_KINDOF(CMDIChildWnd, pFrame);
      //---BEGIN---
      CView *pView = ((CFrameWnd*)AfxGetMainWnd())->GetActiveView();
      
      if (pView  && pView->IsKindOf(RUNTIME_CLASS(CPreviewView)))
       SendMessage(WM_COMMAND, AFX_ID_PREVIEW_CLOSE);
      //---END---
      ((CMDIFrameWnd *)AfxGetMainWnd())->MDIActivate(pFrame);
      //...
      }
      
      HTH!
      Cheers,
      kaosd

      Reply
    Reply
  • Fix for ASSERT in VC6

    Posted by Legacy on 05/17/1999 12:00am

    Originally posted by: Dieter Fauth

    Hi Everybody,
    
    the code snippet below does fix the ASSERT in VC6.
    This ASSERT is not there in VC5.
    Because I do not have VC6 I added a derived ASSERT_VALID
    into CTabBar and added the ASSERT:

    #ifdef _DEBUG
    void CTabBar::AssertValid() const
    {
    // Mimic the behaviour of MSVC6
    ASSERT((m_dwStyle & CBRS_ALL) == m_dwStyle);
    CTabBar_parent::AssertValid();
    }
    #endif

    I do not fully understand why the styles WS_CHILD etc. do cause this
    problem (ASSERT). Anybody out there with a detailed knowledge?
    The fixed create:

    BOOL CTabBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
    {
    ASSERT_VALID(pParentWnd); // must have a parent

    // save the style (some of these style bits are MFC specific)
    m_dwStyle = (UINT)dwStyle & CBRS_ALL;

    // translate MFC style bits to windows style bits
    dwStyle &= ~CBRS_ALL;
    dwStyle |= CCS_NOPARENTALIGN|CCS_NOMOVEY|CCS_NODIVIDER|CCS_NORESIZE;

    // initialize common controls
    VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));

    // create the HWND
    CRect rect; rect.SetRectEmpty();
    return CWnd::Create(STATUSCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID);
    }

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds