Introduction
Toolbars are a great user interface widget, but when an application has a large number of toolbars they can quickly become unmanageable. Providing facilities for the user to toggle toolbars on and off is all very well. The downside is that the use of the facility distracts the user from their task. I ran into exactly this problem in a recent application, and attempted to find a solution. Ultimately I came up with the Tool Group Manager that I present here.
The idea is to have a high level toolbar that present the user with overall options, rather than specific options. On selecting an overall option, another toolbar is displayed subdividing the group into specifics. The last specific option is maintained automatically to minimize user effort.
In the article Changing toolbars dynamically at runtime by Masoud Samimi, it shows how to dynamically replace toolbars at runtime. As it turns out, these two articles are very similar, but it is still appropriate to present my alternative. I do not profess this method to be in any way better than Masoud's, simply an alternative that I developed for my own needs.
How to use the class
As much of the functionality as possible is wrapped into a single template class called TToolGroupManager
. A few changes are must be made to the main window. The class has a very small public interface, consisting of a constructor, destructor and two alternative methods of adding toolbars.
template<class FRAMEWND>
class TToolGroupManager : public CCmdTarget
{
...
public:
TToolGroupManager(FRAMEWND *pFrameWnd);
virtual ~TToolGroupManager();
void AddToolGroup(WORD wID, CToolBar *pToolBar);
void AddToolGroups(const WORD wIDs[], CToolBar *pToolBar, int nCount);
...
};
In the true tradition of MFC, I have declared a macro to make implementation easier. In the frame window of your application, instantiate the template class and add the IMPLEMENT_TOOLGROUPMANAGER
macro. You will also need to create an array of CToolBar
objects for the dynamic toolbar controls.
class CMainFrame : public CFrameWnd
{
private:
CToolBar m_tbTool[3];
TToolGroupManager<CMainFrame> m_ToolGroupManager;
...
IMPLEMENT_TOOLGROUPMANAGER
DECLARE_DYNCREATE(CMainFrame)
DECLARE_MESSAGE_MAP()
};
Now, in you main frame's implementation (.cpp) file, declare an array of
WORD
s containing the resource IDs of the main tool groups (the resource IDs do not have to be contiguous). Next, create a MFC message map for the template containing an
ON_COMMAND
and ON_UPDATE_COMMAND_UI
entry for each of the array entries. Currently this is the easiest way to automate this process, as the information is required at compile time to build MFC's message map array.
const WORD nToolGroup[] = { ID_TOOL1, ID_TOOL2, ID_TOOL3 };
BEGIN_MESSAGE_MAP(TToolGroupManager<CMainFrame>, CCmdTarget)
ON_COMMAND(nToolGroup[0], OnToolGroup)
ON_COMMAND(nToolGroup[1], OnToolGroup)
ON_COMMAND(nToolGroup[2], OnToolGroup)
ON_UPDATE_COMMAND_UI(nToolGroup[0], OnUpdateToolGroup)
ON_UPDATE_COMMAND_UI(nToolGroup[1], OnUpdateToolGroup)
ON_UPDATE_COMMAND_UI(nToolGroup[2], OnUpdateToolGroup)
END_MESSAGE_MAP()
The TToolGroupManager
constructor requires a pointer to the main window, modify your CMainFrame
's constructor as shown below
#pragma warning(disable : 4355)
CMainFrame::CMainFrame() : m_ToolGroupManager(this)
{
}
#pragma warning(default : 4355)
The #pragma
prevents a compiler warning about using the this
pointer in a constructor list
The main frame implementation needs to pass command messages to our tool group manager. Use ClassWizard to add an override for the OnCmdMsg
member function and call the tool group manager class as follows:
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
return m_ToolGroupManager.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo) ||
CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
Finally, in the OnCreate
member function of the main frame, create the toolbars as you would normally. Then add them to the tool group manager. This can be done in one of two ways.
First you can add each toolbar individually, like the following example shows:
int nIndex;
for (nIndex=0; nIndex < sizeof(nToolGroup)/sizeof(nToolGroup[0]); nIndex++)
m_ToolGroupManager.AddToolGroup(nToolGroup[nIndex], &m_tbTool[nIndex]);
Or you can let the tool group manager do the work for you, like the following example shows:
m_ToolGroupManager.AddToolGroups(nToolGroup, m_tbTool, sizeof(nToolGroup)/sizeof(nToolGroup[0]));
The AddToolGroup
method is provided for simplicity, in the case where the toolbars are not in an array. Generally I would suspect that the AddToolGroups
method would be commonly used.
Summation
That sums it up. I hope that some of you find it useful, and easy to use. Please post your comments, good or bad. Preferably good ;)
Craig graduated with a B.SC. Honours in Computing for Real Time Systems from the University of the West of England, Bristol in 1995 after completing an HND in Computing in 1993.