This article shows how to create a Microsoft style header bar and status bar control using simple Common Control library calls and messages. No GDI drawing code is required, as all the functionality for these are already built into the common controls library.
For a recent project, I wanted to create a header bar like Microsoft have on most of the applications on the Pocket PC platform. However I did not want to use MFC, in part because of the size.
The dialog box below is from the Inbox application on the Pocket PC 2002. I have masked the areas not of interest, so you can see the header bar control and the status bar control on the main application window.
The picture has two interesting features on it. The first feature is the header control at the top. This has a dropdown combo box allowing the user to select the sort by option on the right and select the relevant folder and message store to use on the left. To keep the look and feel of the Windows UI, if the user clicks on the drop down, only the dropdown is highlighted. If the user clicks anywhere on the button the entire button is highlighted.
The second feature was the use of the "status bar" at the bottom to allow the user information on the state of the current folder to be displayed, but more on that later.
For the header control bar my first thought was to use the header common control to create the effect in the picture. However this is misleading as I found out after some work with Remote Spy++. What was believed to be a header control is in fact toolbar control.
So now, having established that the header bar control is not in fact a header control, but a toolbar, the question then becomes, how was it done?
A look around the common controls library reveals that the toolbar control does indeed support drop down buttons, but when implementing them the the combo box drop down buttons have a right hand side border that looks unsightly as well as the gap between the left border and the text.
To tackle the second issue is quite simple, when adding the toolbar buttons we want to just display nothing, rather than a blank bitmap. In order to correct this you need to specify that the bitmap to be used is
I_IMAGENONE (which is equivalent to -2) and not -1 which means just display an empty bitmap.
The first issue is more problematic. How are the divider lines to be removed from the combo boxes? Initially I thought that I would need to totally rewrite the drawing code for this in order to get it to work. Fortunately in the common controls header file there is an undocumented define
CDRF_NOVERTBAR. Using this as part of the custom draw stages when the toolbar control is redrawn allows the divider bar to be removed.
The above is a corrected header bar control with the divider lines removed and the blank icon removed. This code now mirrors the functionality of the controls in the standard Windows applications as far as I am aware.
Below is the extract of code for the
WM_NOTIFY handler for the
NM_CUSTOMDRAW function. Note how the common control module's notification handler is told that each item will need to be redrawn whenever an item in the toolbar is to be redrawn. Then, when an item is about to be redrawn the
CDRF_NOVERTBAR code is returned informing the common control module that the current item being drawn is to be drawn without vertical divider bars. Of course it is also possible to do this on an item by item basis by checking the item in the
lItemlParam portion of the
LPNMCUSTOMDRAW lpnmcd = (LPNMCUSTOMDRAW)lParam;
The final section is to handle the command when the toolbar button is clicked. The toolbar button consists of two portions because the button is a dropdown style button. When the dropdown is pressed, the corresponding button needs to simulated as being pressed. To do this the
TBN_DROPDOWN notification command needs to be handled and to post a
WM_COMMAND message to the main window based on which command was selected. When the command associated with the button is clicked, a message is routed to the parent window where it is handled in the
WM_COMMAND handler as would a normal message if the a toolbar button were pressed.
As part of the sample provided, it loads a menu resource and displays it using
TrackPopupMenu. Other things you could do would be to build and display a tree view or similar. In fact the sample source code included in the zip file does exactly that for the left dropdown button, however I have not elaborated the code here.
To handle the status bar, it's a bit simpler in that when creating a status bar, the bar is successfully created however it does not appear on the screen. The problem with this is that the control is set to align to the bottom of the screen and not the top of the menu bar, so when the menu bar is created on the bottom of the screen, it actually obscures the status bar and so the status bar appears invisible, but is really hidden behind the menu bar.
To fix this problem it is a simple case of using the
CCS_NOPARENTALIGN window style when creating the control so that the control does not align with any of the client area. Of course you will then need to manually realign the controls around it to cater for the status bar between the menu bar and the remainder of the controls on the main window. You will also see when the status bar is initially displayed it has a border and default text from when the window is created. We just set the text in the window using the
Using the code
The code in the Win32 sample is just a basic Pocket PC 2002 application with the "Hello World" default implementation.
This was extended to create a new toolbar window at the top of the window. To see how the menu bar control was created, look at the function called
The main points to note here are that the toolbar window has been created with the
TBSTYLE_LIST. The most important being the
TBSTYLE_CUSTOMERASE. This will allow the painting stages to be intercepted and replaced with the new custom draw handler paint stages, allowing the removal of the vertical bars on the dropdown buttons.
Once the toolbar has been successfully created, the two buttons may be added to the control. There are actually three buttons, the middle one's width is calculated as the remaining width after deducting the size of the left and right buttons and just an unpressable standard button.
This code is in the helper function
CreateHeaderRowButtons. The function will allow a left and or a right button, but at least one must be present. The reason
TBBUTTON structures are passed is that it allows the programmer the fullest scope to configure the look and feel of the button.
One important point to note is that when getting the width of the buttons you will need to call
TB_GETITEMRECT rather than
TB_GETBUTTONFINO as this gets the correct width of the auto-sized button whereas the
TB_GETBUTTONINFO does not.
The use of the status bar is more of an oversight than anything else. It is really easy to correct in that it is only the style that needs to be changed from the implicit
CCS_ALIGNBOTTOM to the
CCS_NOPARENTALIGN which then allows the programmer the control to set their own size and position for the status bar in the screen.
The handling of the dropdown control is done in the
TBN_DROPDOWN sub command in the
WM_NOTIFY command handler. This just posts a
WM_COMMAND message of the command associated with the button.
Points of interest
It would also be nice if Microsoft provided samples and documentation on the custom draw stuff for CE as well as documenting the
Fixed download samples problem