When toolbars first appeared in Windows applications, they were commonly assumed to be simple shortcuts to functionality that appeared in the menus. The concept was that a user would (over time) associate a menu item and a toolbar button icon (image) and begin to use the toolbar button instead of the menu item. This had the effect of greatly increasing user productivity for many common tasks like opening a file, saving a file, etc.
Life was good
Then nonbutton controls started creeping into toolbars in Microsoft Office applications in particular. A good example is the Find Text feature. Originally, an icon (typically a pair of binoculars) would appear on the toolbar. When clicked, the Find Text dialog would appear with a range of options for the user case-sensitivity, search direction, wildcards, etc. However, most of the time, a user simply wanted to find a string in the file and didnt care to deal with all of the features presented by the dialog. The user simply wanted to find the string as quickly as possible. So, a search text combo box was added to the toolbar. When text was entered into the edit area of the combo box and the Enter key was pressed, the application would typically search the active document for a match. The most common options for searching were assumed case-insensitive, search forward, no wildcards. If the user clicked the drop-down button on the combo box, a list of recently entered find strings was displayed, allowing for easier reuse of past search values.
When all of this started happening, I remember more than one discussion with engineers I worked with who believed that the whole idea of putting nonbutton controls on a toolbar was a real violation of the intent of a toolbar that it should be an iconic version of menu items. One reasonable complaint was that it cluttered up the toolbar since these combo boxes took up a great deal of space relative to normal toolbar buttons. However, time passed and nonbutton controls became much more common on toolbars. If used judiciously, they can be a real time saver for users and when you get down to it, thats what toolbars were really intended for a list of commonly used features that could be accessed quickly.
If youve worked very much with toolbars in Visual Studio (even the newer .NET versions), youve probably noticed that the editor doesnt have any native support for dropping nonbutton controls like combo boxes on a toolbar. Fortunately, it is possible to do this and it turns out to be quite straightforward.
First, consider the following main window from a simple MFC application I created:
This is a plain-old baseline MFC application created using App Wizard with one modification: I added a new toolbar button at the end of the toolbar. Its the one that looks like the letter X. Creating this extra button is the first step in putting a control on the toolbar. To be fair, you could create a nonbutton control without doing this step, but I think its easier to do it this way since you get to use the Visual Studio Resource Editor to visually position the control.
The next step is to derive a new version of the MFC toolbar class CToolBar
.
I called mine CEnhancedToolBar
. Within this class, I created a method
to allow for creating a single combo box given a toolbar button ID and an ID
for the new combo box. Here is what that code looks like:
bool CEnhancedToolBar::CreateCombo( int nToolBarBtnID,int nComboID ) { // only allow a single call to this method since we can only // handle a single combo box. if ( ::IsWindow(m_combo.m_hWnd) ) return false; // Create the combo box int nIdx = CommandToIndex(nToolBarBtnID); ASSERT( nIdx >= 0 ); SetButtonInfo(nIdx, nComboID, TBBS_SEPARATOR, 150); const int nDropHeight = 100; CRect rect; GetItemRect(nIdx, &rect); rect.top = 1; rect.bottom = rect.top + nDropHeight; if (!m_combo.Create(CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_TABSTOP | WS_CHILD, rect, this, nComboID)) { TRACE0("Failed to create combobox in ToolBar\n"); return false; } // update the combo with a font. m_font.CreatePointFont( 80,"MS Sans Serif" ); m_combo.SetFont( &m_font ); // show the combobox. m_combo.ShowWindow( SW_SHOWNORMAL ); return true; }
After making sure this method is only called once, the first step is to find
the toolbar button that we want to replace and change its type to be a Separator.
This allows the toolbar button to be used to separate the new combo box from
surrounding buttons and looks better visually. Then, the combo box is created
just as you would create any MFC CComboBox
object. Notice that the combo
box uses the toolbar as its parent as you would expect. Then, I gave the combo
box its own font object to make it look nicer (yes, if I was doing this in a
real app, Id query the current Windows System properties to take into
account overrides for control fonts, but you get the idea). Finally, I show
the combo box on the toolbar.
Back in MainFrm.cpp where the toolbar is created, I added a few lines of code to insert some items into the combo box just for fun:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } const int ID_COMBO_BOX = 2000; if ( m_wndToolBar.CreateCombo( ID_COMBO_BOX_BTN,ID_COMBO_BOX) ) { CComboBox& box = m_wndToolBar.GetCombo(); box.AddString( "blue" ); box.AddString( "green" ); box.AddString( "yellow" ); box.SetCurSel(1); } // addl init stuff follows... }
Finally, here is what the toolbar with the new combo box looks like:
And one final look with the box dropped down:
Since the combo box is parented to the toolbar, all of its notification messages
will go to the toolbar. So if you want those notifications to be processed by
the main window (or some other code), you'll need to add some more support in
CEnhancedToolBar
to pass those notifications on to the interested party.
An exercise left for the reader is improving the CEnhancedToolBar
class
so that it supports creating any number of CComboBox
objects on the toolbar
(not just a single one).
At this point, it should be clear that adding nonbuttons to toolbars is really simple. If you have a Find feature in your application, there's no reason not to include a combo box Find control on your toolbar, too. Your users will thank you.
Mark M. Baker is the Chief of Research & Development at BNA Software located in Washington, D.C. He can be contacted at [email protected].