Creating a Tabbed Tool Bar

Environment: compiled using VC6 on WinXP

Recently, I was involved in a project to create a 3D application. At that time I noticed the tabbed toolbar of 3D Studio Max 4 (as shown below).

Because my application required the use of a lot of tools (just like 3DS Max), I decided to implement this control. Here I shall share with you the most important details of creating the tabbed toolbar.

The implementation of the control is quite simple, actually. The control is primarily a Dialog Bar that manages a tab control and a Tool Bar. Thus, the class is derived from CDialogBar and it creates the CTabCtrl and CToolBar objects. The actual creation of the Tab control and toolbar occurs in the Create function of the Dialog Bar.


BOOL CTabToolBar::Create(CWnd* pParentWnd)
{
if(CDialogBar::Create(pParentWnd,IDD_TBTLBAR_DLG,
CBRS_TOP|CBRS_TOOLTIPS|CBRS_FLYBY,
AFX_IDW_TBTLBAR))
{
// create the tab control.
if(m_wndTabCtrl.Create(TCS_TABS | TCS_FIXEDWIDTH |WS_CHILD |
WS_VISIBLE, CRect(0,0,0,0), this,
IDC_TBTL_TAB))
{
// create tool bar.
if(!m_wndToolBar.CreateEx(&m_wndTabCtrl))
return FALSE;

return TRUE;
}
return FALSE;
}
return FALSE;
}

Note that the toolbar object is a child object of the Tab control object. There are two issues that need to be noted at this point: The Tab control has to fill the entire area of the Dialog Bar and the Toolbar should be positioned comfortably within the Tab control. This is taken care of by the OnSize function of the Dialog Bar.


void CTabToolBar::OnSize(UINT nType, int cx, int cy)
{

if((m_wndTabCtrl.GetSafeHwnd() != NULL) &&
(m_wndToolBar.GetSafeHwnd() != NULL))
{
// Get dialog bar’s current dimensions.
CRect rcWnd;
GetWindowRect(&rcWnd);

// Redraw Tab Control
m_wndTabCtrl.SetWindowPos(NULL, 0, 0,rcWnd.right,
rcWnd.bottom, SWP_NOZORDER |
SWP_SHOWWINDOW);

// get the new toolbar position.
CRect rcClient;
m_wndTabCtrl.RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST,
0,reposQuery,rcClient);
rcClient.OffsetRect(2,2);

// Redraw the toolbar.
m_wndTabCtrl.RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST,0,
CWnd::reposDefault,NULL,rcClient);
}
}

In the code above, I would like to point out the function RepositionBars. This is a function that positions the toolbar within the Tab control. It can also be used to retrieve the client space within the Tab Control as shown in the code above.

Now what we need is to add functionality at the Change Tab events. Because in MFC there isn’t any such event notification (please correct me if I am wrong), we would have to create that event ourselves. To do this, I have subclassed the CTabCtrl and defined the following variable.


// in the CFlatTabCtrl
#define USR_TABCHANGED (WM_USER+10)

As you might have noticed, I have named the class CFlatTabCtrl. Well, I thought that while I am at it, I might as well throw in a few gimmicks and made a Flat Tab Control. (Please refer to other articles for the explanation. Implementing an owner drawn Tab Control is a particularly good article for this gimmick.) Anyway, back to the problem at hand. Next, I had to check for mouse clicks on the Tab control and pump the message to the parent window (the Dialog Bar). The Dialog Bar then has to change the tab event notification that is handled by the OnChangeTab function.

Now, I would to jump to another important part of the implementation first: adding the tabs and toolbars. To facilate this, I have created the function AddItem, which primarily accepts in the tab name, toolbar resource id, and the resource id for the toolbar bitmap. An example of this can be found in the virtual function OnTabToolBarCreated. (The OnTabToolBarCreated function is called in the Create function of the CFlatTabCtrl class.)


void CTabToolBar::OnTabToolBarCreated()
{
AddItem(“Tab 1”, IDR_TABTOOLBAR_1, IDB_TOOLBAR_HOT_1,
IDB_TOOLBAR_COLD_1);
AddItem(“Tab 2”, IDR_TABTOOLBAR_2, IDB_TOOLBAR_HOT_2,
IDB_TOOLBAR_COLD_2);
}

This would have created a list of toolbar attributes to choose from when the tabs are selected. Back in the OnChangeTab function, all we have to do is to load the appropriate toolbar attributes and we are done.


void CTabToolBar::OnChangeTab(UINT nTabIndex)
{
if((int)nTabIndex >= m_nItems) return;

// Load the tool bar buttons.
TTBITEM* pTabToolBarItem = m_pTabToolBarItems[nTabIndex];
if(!m_wndToolBar.LoadToolBar(pTabToolBarItem->uToolBarResID))
return;

// Set the tool bar bitmaps.
m_wndToolBar.SendMessage(TB_SETIMAGELIST, 0,
(LPARAM)pTabToolBarItem->hColdBitmap);
m_wndToolBar.SendMessage(TB_SETHOTIMAGELIST, 0,
(LPARAM)pTabToolBarItem->hHotBitmap);

// Set the new button sizes.
m_wndToolBar.SetSizes(CSize(31,30),CSize(24,24));
}

Oh, there is actually one more thing to do. Because the Toolbar is a child of the Tab control, all command messages (such as the toolbar button clicks) are pumped to this control’s class only. If you want the view class of the application to handle the messages, just route them to the CMainFrame class. I have implemented this by overriding the OnCmdMsg in both the Tab control and the Dialog Bar. This way, the messages will get pumped to the CMainFrame class. After this, MFC will take care of routing the messages to the respective CView classes.


BOOL CFlatTabCtrl::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
if(GetParent()->GetSafeHwnd())
return GetParent()->OnCmdMsg(nID, nCode, pExtra,
pHandlerInfo);

return CTabCtrl::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

I guess that’s it for this article but please note that this control is still far from the 3D Studio Max’s Tabbed Toolbar. For example, toolbar customization and dockable toolbar (tabs can be torn off the tabbed toolbar and they would be presented as dockable) are some of the features that are not implemented in this control. I hope somebody would do that and post it up in Codeguru.

Downloads


Download demo project executable
– 26 Kb


Download demo project source
– 87 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read