Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / MFC
Article

Programming Toolbar Chevrons

Rate me:
Please Sign up or sign in to vote.
4.87/5 (18 votes)
8 May 2003 419.8K   6.4K   150   79
An introduction to using the cool new toolbar chevrons
  • 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

    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.

    1. 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.

    2. 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)
      	//{{AFX_MSG_MAP(CChevBar)
      	//}}AFX_MSG_MAP
      
      	// Reflection message entry for Chevron push
      	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;
       
      	// Has the band id of the chevron that generated this message
      	int iBand = pChev->uBand;
      
      	// Do what ever is needed when the chevron is pushed, 
              // usually a popup showing the hidden button will be 
              // shown, Rebar doesn't do it, it is our JOB ! 
      	// Well what are we there for ? :-)
      }
    3. 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.

    • Create an SDI or MDI project with the Rebars enabled
    • Copy the four files ChevBar.cpp, ChevBar.h, ChevDrop.cpp, ChevDrop.h and include it in the project.
    • Change the default CReBar class in the Mainframe(or anywhere relevant) to CChevBar
    • In the code that adds the child bars to the Rebars, modify the AddBar() call as shown below:
      if ( !m_wndReBar.Create(this) ||
              // 0 is the Band id, it has to be unique
              !m_wndReBar.AddBar(&m_wndToolBar, 0 ) || 
              // 1 is band id it has to be unique
      	!m_wndReBar.AddBar(&m_wndToolBar1, 1 ) || 
      	!m_wndReBar.AddBar(&m_wndDialogBar) )
      {
      	...
      }
    • Compile and execute the application. Your application should have the chevrons handled correctly now, just like the way Internet explorer 5.0 does.

    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.

    Image 2

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

    Image 3

    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:

    ConditionAction required
    A button in the toolbar was clickedThe 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):

    1. 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.

    2. 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).

    3. 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.

  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    India India
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    QuestionCan not add string on the button of CToolBarCtrl(m_tb) when clicking the chevron??? Pin
    sea_ldh6-Dec-11 14:17
    sea_ldh6-Dec-11 14:17 
    GeneralBugfix for GetMaxSize Pin
    Ștefan-Mihai MOGA31-Mar-11 1:22
    professionalȘtefan-Mihai MOGA31-Mar-11 1:22 
    GeneralToolbar position Pin
    mewadale15-Nov-09 19:20
    mewadale15-Nov-09 19:20 
    QuestionHow change Docking position for additional toolbars ? Pin
    Gobsek29-Jun-09 0:11
    Gobsek29-Jun-09 0:11 
    Questioninserting gripper into toolbar/toolstrip Pin
    rixwan19-Nov-08 3:17
    rixwan19-Nov-08 3:17 
    GeneralKeyboard navigation issues. Pin
    benjamin2317-May-08 23:18
    benjamin2317-May-08 23:18 
    First, let me say this is a greate article and the code it's verry well written.

    I've noticed that the IE chevron poped up menu it's not quite keyboard navigation friendly, it's not quite what you would expect from a regular menu which we all know it isn't.
    For example if there's a sub menu you can't use left & right keys to go back and forwad.
    There are limitations to this fake menu.
    I've found that your idea of poping up a toolbar like ui insted of a menu like ui works much better because the user would not be confused.
    GeneralRussian translation of this article Pin
    amdtm22-Jan-08 10:27
    amdtm22-Jan-08 10:27 
    QuestionHow prevent the IE to put a item in Chevron of frequently used toolbar icons? Pin
    Marcelo Calado12-Nov-07 5:03
    Marcelo Calado12-Nov-07 5:03 
    AnswerRe: How prevent the IE to put a item in Chevron of frequently used toolbar icons? Pin
    KarstenK9-Jul-08 23:04
    mveKarstenK9-Jul-08 23:04 
    QuestionCan not compile demo project Pin
    s196675m22-Oct-07 4:52
    s196675m22-Oct-07 4:52 
    QuestionToolbar buttons are displayed partially Pin
    Tushar Jadhav26-Jun-07 23:54
    Tushar Jadhav26-Jun-07 23:54 
    AnswerRe: Toolbar buttons are displayed partially Pin
    69625-Nov-07 19:56
    69625-Nov-07 19:56 
    GeneralSubclassing internet explorer main window Pin
    Cosescu Bogdan26-Mar-07 6:21
    Cosescu Bogdan26-Mar-07 6:21 
    GeneralRe: Subclassing internet explorer main window Pin
    Cosescu Bogdan26-Mar-07 20:40
    Cosescu Bogdan26-Mar-07 20:40 
    GeneralRe: Subclassing internet explorer main window Pin
    Dabara14-Aug-09 5:18
    Dabara14-Aug-09 5:18 
    Questionhow to catch, which buttons was clicked? Pin
    StickThai27-Nov-06 17:17
    StickThai27-Nov-06 17:17 
    Questiongood code Pin
    StickThai26-Nov-06 15:01
    StickThai26-Nov-06 15:01 
    GeneralA mismatch with IE Pin
    zhou_wz20-Jul-06 20:09
    zhou_wz20-Jul-06 20:09 
    Generalthank you Pin
    lupyhlp092516-May-06 15:17
    lupyhlp092516-May-06 15:17 
    GeneralOffice Toolbar chevron Pin
    HakunaMatada15-Apr-06 2:23
    HakunaMatada15-Apr-06 2:23 
    GeneralRe: Office Toolbar chevron Pin
    trongdu17-Nov-06 22:33
    trongdu17-Nov-06 22:33 
    Questionsolution without imagelist? Pin
    tim63514-Mar-05 1:45
    tim63514-Mar-05 1:45 
    Generalicons in chevron menu Pin
    G.A.21-Oct-04 1:36
    G.A.21-Oct-04 1:36 
    GeneralRe: icons in chevron menu Pin
    cnshines12-Feb-06 1:58
    cnshines12-Feb-06 1:58 
    Generalchevron is never shown Pin
    bgeradz29-Jul-04 8:51
    bgeradz29-Jul-04 8:51 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.