Introduction
Following on from my previous article on creating header bar controls, I decided to provide an implementation in MFC that had the quality and level of support a standard common control in the MFC class library would have. Developers would be able to use the class as though it were to all intents and purposes a standalone class.
Background
The header bar control class had three basic design goals:
- Integrate and use the MFC class library.
- The class should not require the developer to use any hooks or callbacks or to do anything special to use the class.
- The code should be as lightweight as possible and work across as many variants of Windows CE as possible.
Very briefly the Header bar control should be able to add a button or dropdown button to the left and or right hand side of the main window. This button should then send a command to the main frame window which will be routed to the current view or document as would any normal message in MFC.
In the Inbox application the header bar control is a toolbar common control so it is now a choice as to whether the new class is derived from CToolbarCtrl
or from CWnd
. It was decided that the new control would be derived from CWnd
for the following reasons. First the toolbar contains substantial functionality that is not relevant to the header bar control. Secondly if the toolbar control were to be used, it would require that the base class be made private or protected which is difficult to use and explain in MFC. Finally what I felt was that the control should be a light weight wrapper around a toolbar window and the toolbar control had too much overhead that it obscured what I was attempting to do.
To this end, the code for the toolbar control was copied and all the functions not relevant to the header bar control were removed. Then public functions to add a button to the left and the right were provided.
One word of warning, in order to keep with the style of MFC windows, the create function takes as a first parameter, a style parameter and so this parameter cannot be defaulted. There is a define at the top of the HeaderBar.h file HEADERBAR_DEFSTYLE
which has the recommended default styles. Internally the only mandated styles are CCS_NODIVIDER
and TBSTYLE_CUSTOMERASE
.
AddLeftButton
and AddRightButton
functions are provided to add a button to the left and or right of the header bar. Implicit in this is the assumption that the button IDs will never be zero as this ID is reserved for the separator button and furthermore the user will add only one button to the left and or right, so there will be at most three buttons in the header bar control. If any of these conditions are not met, then an assert will fire.
When a button is added to either the left or the right, the size of the separator button is recalculated using the function AdjustSeperatorButtonSize
.
The actual code to add the buttons to a toolbar is exactly the same as adding a normal toolbar button and so has not been elaborated here.
The next phase of development is to handle the custom drawing of the separator bars so the dropdown buttons appear as part of the main dropdown button and not separated by a divider bar. The easiest way to link the custom drawing to the control is to use message reflection. This has been covered in the Win32 documentation provided with MFC for Win32, under technote 62.
We use the ON_NOTIFY_REFLECT
macro to handle the NM_CUSTOMDRAW
message within the control. MFC gives our control the first chance at handling the notification macro before it passes it to the parent window, which in this case is the frame window. The custom draw message then tells the common control library to paint each item in the toolbar (CDDS_PREPAINT
stage returns CDRF_NOTIFYITEMDRAW
). Then when the CDDS_ITEMPREPAINT
stage is called, it returns CDRF_NOVERTBAR
. This tells the common controls library to not draw a divider bar for dropdown buttons in the toolbar.
The final phase of development is to ensure that the header bar control can successfully be made to exist with the document view architecture in MFC. When Windows needs to resize the main frame it calls the RecalcLayout
function which then repositions the control bars using the unsurprisingly named RepositionBars
function. This calls then gets the rectangles occupied by the child controls which are not the view between the default range of 0 and 0xFFFF by posting a WM_SIZEPARENT
message to the window. This also gives the control the opportunity to reposition itself correctly. The remainder of the space in the frame is then allocated to the view.
Thus the implementation of the header bar has been concluded. There are only two files that need to be added to the project. The first is HeaderBar.h which is the header file for the control, and HeaderBar.cpp which is the implementation. Really the single line helper functions in here should be put into their own .inl file but this has yet to be done, primarily as it would detract from the code. The header file has been laid out using doxygen formatting. There are many comments and asserts in the implementation file which should highlight any problems or answer questions about what the code is doing.
Using the code
Using the CHeaderBarCtrl
is a pretty straight forward exercise. Normally the bar will be created in the main frame window in the OnCreate
function. Create a member variable of the CHeaderBarCtrl
class in the CMainFrame
class, then create a new window instance in the OnCreate
function, remember to set the style, as this is not automatically set. (You can use the HEADERBAR_DEFSTYLE
define as a default) Next add the bitmap if you are using one and any strings, finally add the left and right buttons to the header control and set the owner. That is it for creating and setting up the control.
Next add the left and right buttons using the AddLeftButton
and AddRightButton
commands.
The final step that is required is to add in command handlers for when the button is pressed. These can be done in the doc or in the view. Personally I prefer doing this is in the view and this is how its been done in the implementation.
In the provided sample there is a dropdown button on the right and a treeview on the left. This sample is almost the same as the original supplied with the Win32 implementation, just that it has been modified to be a true MFC port and now the only way to dismiss the tree control is to click on the left button.
Points of interest
The WM_SIZEPARENT
function is useful if you want to put a window so it can share space and coexist with the view in the frame.
The ON_NOTIFY_REFLECT
macro is very useful if you want to encapsulate notification methods in your own class, rather than having to paste code into the handlers of the parent class.
History
None so far.