Download source files - 18 Kb
See article addenda for more latest updates
Introduction
I guess I know what you're thinking-"Oh no!! Not another toolbar article!". Rest assured that this article discusses something that's most probably not found in any of those handy (dog-eared) MFC/SDK books you've got. It's about a very cool toolbar feature called chevrons. I hope you'll enjoy this, and incorporate the feature into your own code. Adding Chevrons to your code gets your app yet another step towards that elusive professional standard most commercial programs exude.
Chevrons
Version 5.80 of the common control library added support for a cool feature to the rebar control called chevrons. When the length of a band is lesser than the size of it's child control, the rebar displays a chevron to indicate that there's more to show. This feature is particularly useful when there are more bands in the rebar and the user requires more working space in the application. In addition, the user can intuitively see that there are more items that are hidden. This article shows you how to incorporate chevrons and how to handle the same.
To see chevrons at work, fire up IE and resize the window so the width of IE rebar is shrunk to the point it clips some of its items. Now you should see the the chevron(>>), to indicate that some items are not completely visible.

Chevrons can also been seen on the Task bar when you have the quick launch deskband option enabled (Win 98). When the chevron is clicked on, a floating toolbar that has the buttons/options that are not shown in the band appears.
Programming Chevrons
The folks who gave you the common controls have already done much of the work for you. As you might have guessed, there is a flag to activate the chevron style, as well as a handler that's called when the chevron is clicked. Here's how to get it all up and running.
- When adding a band to the rebar control, the flag RBBS_USECHEVRON has to be OR'ed in with the style:
if (!m_wndReBar.Create(this ) ||
!m_wndReBar.AddBar(&m_wndToolBar, NULL, NULL, RBBS_USECHEVRON )||
!m_wndReBar.AddBar(&m_wndToolBar1, NULL, NULL, RBBS_USECHEVRON ))
This code adds two bands capable of supporting chevrons.
- Next, you have to set the size the chevron should be displayed in, when the band grows or shrinks:
REBARBANDINFO rbbi;
CSize sizeBar;
m_wndToolBar.GetToolBarCtrl().GetMaxSize (&sizeBar);
rbbi.cbSize = sizeof(rbbi);
rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE | RBBIM_ID;
rbbi.cxMinChild = 0;
rbbi.cyMinChild = sizeBar.cy;
rbbi.cx = rbbi.cxIdeal = sizeBar.cx;
rbbi.wID = 0;
m_wndReBar.GetReBarCtrl().SetBandInfo(0, &rbbi);
m_wndToolBar1.GetToolBarCtrl().GetMaxSize (&sizeBar);
rbbi.cbSize = sizeof(rbbi);
rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE | RBBIM_ID;
rbbi.cxMinChild = 0;
rbbi.cyMinChild = sizeBar.cy;
rbbi.cx = rbbi.cxIdeal = sizeBar.cx;
rbbi.wID = 1;
m_wndReBar.GetReBarCtrl().SetBandInfo(1, &rbbi);
Note that each band that might display a chevron can have its style modified with the SetBandInfo()
call.
The flag RBBIM_IDEALSIZE has to be set so that the ideal size for the chevron can be specified. This flag indicates that the cxIdeal member holds the ideal size of the band. When the band size is reduced below this value, a chevron is shown. You can experiment with this by changing:
rbbi.cx = rbbi.cxIdeal = sizeBar.cx;
to
rbbi.cx = sizeBar.cx;
rbbi.cxIdeal = sizeBar.cx/2;
You will find that the chevrons are shown only when the size of the band is half the length of the toolbar.
RBBIM_CHILDSIZE specifies the child window size information of the band. For the chevron to appear, at least both the above flags must be specified.
The RBBIM_SIZE flag has been set so that the bands are initially displayed with this size. If you don't use this flag, then the bands won't stay right next to each other-provoking complaints from users!
RBBIM_ID is a very useful and important flag that must be set. Of course, the member wID should also be set to a unique value, so that you can identify the band when required. This is because when users drag the band around, the index of the band keeps changing depending on the current position. But the band id stays with the band no matter where it goes.
NOTE: These two steps are just for convenience, it may be possible to set it all in one go.
Well, at this point your app should display the chevron when required. However, we still aren't done yet; we need to take care of what happens when the user interacts by clicking it. We have to write the handler code. Really, what you do in the event is most likely up to you, but here, we shall go with the most natural behavior!
Assuming CChevBar
is derived from CRebar
, add a message map for the chevron push event:
BEGIN_MESSAGE_MAP(CChevBar, CReBar)
ON_NOTIFY_REFLECT( RBN_CHEVRONPUSHED, OnChevronPushed )
END_MESSAGE_MAP()
Here's the message handler:
void CChevBar::OnChevronPushed( NMHDR * pNotifyStruct, LRESULT* result )
{
NMREBARCHEVRON* pChev = (NMREBARCHEVRON*) pNotifyStruct;
int iBand = pChev->uBand;
}
-
The important value that is needed is the uBand member of the structure NMREBARCHEVRON. This identifies the band whose chevron was clicked. Once we have the band id, we can get all the information about the band. Usually, we would need the child window handle.
Chevrons are usually placed when the toolbar is the child window of the band, but of course, this need not always be the case.
What do we do when a chevron is pushed? 99% of the time, we'll be dealing with a toolbar as the child window for the band. So let's stick with this at the moment. Okay, now let's say we wanted to display a popup menu for the items that are hidden when the Chevron is clicked. But how would we find the hidden buttons in the toolbar?
Here's what to do to get those hidden buttons:
- Obtain the band rectangle (subtract chevron width if you want to); let it be R1
- Get the toolbar button rectangle, let it be R2
- Check if the intersecting rectangle of R1 and R2 is same as the R2.
- If they intersect correctly then the button is shown
Of course, this algorithm has to be changed when the child window of the band is not a toolbar.
The simple example application provided along with this article adds the hidden items to an owner drawn popup menu and it displays the popup menu just below the chevron. In the popup menu's drawitem method, the bitmap of the button is extracted from the toolbar's image list and then drawn. The text is extracted from the tip text part of the string resource, which has the same id of the menu item.
Conclusion
Phew! Hopefully by now, you've got a pretty good feel of what chevrons are and how they're implemented. I hope this article has whetted your appetite to find out more and dig in the goodies available in Windows! Happy programming!
Known Problems
As is always the case, there're some issues that haven't been resolved at the moment. If you find out ways of circumventing these problems or have any ideas, I'd appreciate it if you let me know-or better still, post your own article for others to see!
- Toolbar buttons with the drop down style are not handled properly (you can check this with IE, IE does it very nicely). Usually, when the dropdown style toolbar button is clicked another popup menu will be displayed with more options. Since we are using owner drawn menus and there is no way to get a new menu, our application stumbles here L My way of solving this would be to write a custom menu that has buttons. This would send a similar notification to the parent like the toolbar, when its button is clicked.
- Another problem lies in writing reusable code. Rebar is a control, toolbar is another control. The rebar may not have a toolbar as its child in the bands. If so, then how do we write reusable code/class that works for all sorts of different child controls in the rebar?? Go figure.
- I am sure most of you must've worked with BandObjects. When this is the case, how are we notified of the chevron click, and how do we handle it?
- The initial size setting for the band works fine when the toolbar doesn't have any buttons with the style flag TBSTYLE_DROPDOWN. But when a toolbar has one, the GetMaxSize() function seems to return a lesser value. Is there any other way to set the initial size?
Programming Toolbar Chevrons - An Update
Download source files - 8 Kb
Download updated demo project - 26 Kb
This update serves to rectify a few problems arising in the original "Programming Toolbar Chevrons" article. In essence, it's an update on the way the chevron is handled. Primarily, this is as close as you can get to simulate Internet Explorer 5.0's chevron handling.
The original article describes the technique of using a popup-menu to display the hidden buttons in a tool bar. As a result, the dropdown buttons are not handled correctly, as there is no way to know the action of a button down event.
The idea now is to show a popup window that has a toolbar containing the hidden buttons. This ensures that the new toolbar behaves just as the original one.
Let's see how to use project files and save the explanation for later.
The best thing to see chevron at work is the buttons with TBSTYLE_DROPDOWN
set. For a sample, applications like word, Outlook express can be checked. When the main window is resized smaller than the toolbar width, the chevron appears, when the chevron is clicked, the toolbar buttons that are with "dropdown" style, also has "dropdown" style in the popup window. A mouse click action on this "popup" toolbar simulate the mouse click in the real toolbar button. This is very convenient when a high "working screen" space is required.

The sample application with this article displays a popup in response to the chevron click.

Lets see how it all goes...
Its code time
The idea is simple:
- Create a popup window
- Create a toolbar with the popup as the parent and add the hidden buttons to it
- Show the popup window just below chevrons
- Handle messages in the pop-up window sent by the toolbar. Redirect them if necessary
However, as usual, the implementation of the idea gets rather challenging:
The first thing to see is the CChevBar::OnChevronPushed()
member. This is called by the framework when a chevron button is clicked. In response to it we have to check if the child window is really a toolbar (at this point, we don't know how to handle bands with other controls in it). If it is a toolbar a new CChevDrop
is created-it is this CChevDrop
object that drives the rest of the program.
CChevDrop::ShowPopup()
is the next function that is called which:
- Creates the popup window
- Creates the toolbar inside this popup window
- Copies the image list from the real toolbar to the one in popup
- Inserts the hidden buttons from the real toolbar to the one in popup
And that's it! Your chevron popup is now displayed. But hang on to your hats - it won't start working immediately. This is because the toolbar sends all the messages to the parent, which happens to be the popup window. Hmmm� what if we tell the toolbar to send the notifications to the frame window itself ?? Yes, that works fine, but the popup won't go away once you click the toolbar button. Therefore, you've got to handle the notifications in the popup and send only the required ones to the parent frame. All these are tabulated as follows, for your convenience:
Condition |
Action required |
A button in the toolbar was clicked |
The toolbar sends a command message to the parent.
This means an action is going to be taken and our popup window with the toolbar has to be closed. So pass the command message to the parent frame and close/destroy our popup window. |
The dropdown arrow was clicked for the button with TBSTYLE_DROPDOWN |
The toolbar sends a notification message to the parent.We have to redirect the notification to the parent frame to let it handle. Typically the parent frame handler shows another popup menu full of options. We should not close the popup |
A mouse button click anywhere outside our popup menuOr The escape key was pressed |
We have to close the popup menu |
That's all that's required to handle the chevron popup
A few problems (uh oh, here we go again):
- For the chevrons to show up correctly, the ideal size of the band has to be set. But it was difficult to find the size of the toolbar properly when the toolbar has buttons with the
TBSTYLE_DROPDOWN
style enabled.
CToolBarCtrl::GetMaxSize()
returns the correct size of the toolbar when the toolbar has NO TBSTYLE_DROPDOWN
buttons. But it always seems to return something smaller than the actual size when the buttons have TBSTYLE_DROPDOWN
style enabled. It looks like the width of TBSTYLE_DROPDOWN
style buttons are not taken into account.
Another attempt of mine was to obtain the item rectangle for the first and last buttons. From this, the size of the toolbar can be calculated. But this too doesn't seem to work. The returned size was less than the actual size.
Yet another try was to use CToolBarCtrl::SetRows(CtoolBarCtrl::GetRows(), FALSE, &rectTool)
, but it's a sad thing that it didn't work too.
If anybody gets around the way to find the size of the toolbar, please comment on it or please refer me to it. I'd very much appreciate it.
- Usually the menu scrolls ( "Animates" ) to display its contents, but this functionality has not been implemented at the moment. This is left to the reader as an exercise (wicked grin).
- Usually when a popup menu is shown, the main window(or parent) is not deactivated... But in this case it is deactivated. A problem might occur when the parent frame does not expect this to happen...
A few final words:
The code has been commented with the drawbacks and workarounds. If you think there's a better way to reach the same goal, please do post it. It would helpful for all.