Automatic Tab Bar for MDI programs
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!
Date Last Updated: May 14, 1999

Comments
Lightweight smart â Nike Loose TR Fit in shoot up 2013 3 series
Posted by Tufffruntee on 04/20/2013 03:49amNike 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.
Replywhy the screen always flashes when we switch the Tab?
Posted by Legacy on 11/29/2001 12:00amOriginally posted by: pear Zhang
why the screen always flashes when we switch the Tab?
ReplyHow to add a tab item manually?
Posted by Legacy on 06/12/2001 12:00amOriginally posted by: Martin Unsin
ReplyDemo Download not working properly
Posted by Legacy on 04/19/2001 12:00amOriginally posted by: Praveen Rao
We downloaded ur demo , but it's not working properly.
ReplyCan u please tell us clearly , how we can do these tabbed views.
The case of the disappearing tabs...
Posted by Legacy on 04/02/2000 12:00amOriginally 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
ReplyThanks and Integration with Kirk Stowell's DevStudio-like sample
Posted by Legacy on 07/21/1999 12:00amOriginally 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.
ReplyToggling the tab bar on and off
Posted by Legacy on 06/02/1999 12:00amOriginally posted by: Rick York
Reply
Try this -
Posted by Legacy on 05/19/1999 12:00amOriginally 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
-
-
ReplyRE: RE: Try this - (Print Preview Problem - FIXED)
Posted by kaosd on 05/22/2007 06:35amOops... my apologies for the poor formatting on the previous post... it was my first post here :o) Cheers, kaosd
ReplyRE: Try this - (Print Preview Problem - FIXED)
Posted by kaosd on 05/22/2007 06:31amHi... 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, kaosdReplyFix for ASSERT in VC6
Posted by Legacy on 05/17/1999 12:00amOriginally posted by: Dieter Fauth
ReplyAdd one line --> m_dwStyle &= CBRS_ALL;
Posted by Legacy on 05/16/1999 12:00amOriginally posted by: Masaaki Onishi
Hi.
At the debug mode problem, I fixed liek this.
BOOL CTabBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
ASSERT_VALID(pParentWnd); // must have a parent
// create the HWND
CRect rect;
rect.SetRectEmpty();
// I added these two lines.
m_dwStyle &= CBRS_ALL;
ASSERT((m_dwStyle & CBRS_ALL) == m_dwStyle);
return CWnd::Create(STATUSCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID);
}
If I don't add m_dwStyle &= CBRS_ALL, ASSERT gives me assertion failure. Otherwise, it is OK.
I confirm both debug and release.
After I check MSDN help about m_dwStyle problem,
it seems to come from unused style prolem as well as
bits - lower bits problem like Window3.1?
If we can't use debug mode, we have much trouble to solve
the error from execution.
Regards.
-Masaaki Onishi-
Reply
Loading, Please Wait ...