Article submitted by Vegar Vikan
I have been following Jansen Harris’ blog on the new Office interface from the start, and must say that the new look pleases me. As always, new styles from the Office-crew soon will be adapted by other application developers. DevComponents, with their DotNetBar package, gets credit for being the first to deliver a fully featured Office 2007 Ribbon-control. In this article I will show you how you can make your menu items take on the new look.
In this article, I will be focusing on what Jensen calls "Rich Menus" . There are two noticeable new features about them that I would like to reproduce in my applications. It has "textual separators" instead of the plain lines to break up the menu and it provides a short description of the menu item within the menu itself; like an "inline hint" or something.
With this article, I will attach a sample application with a propup-menu showing the same items as the freeze-menu from Excel, both with standard and custom painting.
Taking control over menu paintingThere are two events on the TMenuItem class we need to act upon to get where we want. The OnMeasureItem event gives us a chance to tell how much space we need for out menuitem. The actual painting is done in the OnDrawItem event. We will also need to enable the custom painting by setting the OwnerDraw property of the menu component to true.
A separator is made by setting the caption-property to a single hyphen. Given a TMenuItem-instnace, you can check the return value of IsLine() to see if it is to be treated as a separator or a regular item. If we want text instead of lines as separators, we cannot use the caption-property since that will break the logic making it a separator. Instead, we will be using the Hint property for this text.
So after filling the Hint property, we can start the actual work. First we will write some code to calculate the size of our new separator. It is done in the OnMeasureItem event handler. We get a reference to the item and its canvas, and need to set the width and height parameters to the calculated values.
As you can see (in the code), we use DrawText from the Windows API with the DT_CALCRECT flag to calculate the needed space for the given text and formatting. We also add some spacing around the text and an extra leading before the text.
When this is done, the menu component will put aside room for us to paint our separator in. The OnDrawItem eventhandler will receive a canvas and a rectangle with the specified size from the OnMeasureItem-event. It will also receive a flag, stating if the menuitem is selected or not. Of course we will be ignoring this flag when drawing separators.
As the code shows (N2DrawItem procedure), we first fill the entire space with our chosen background color, and then we add some effect by drawing two lines in different colors at the bottom. The last part of the code deals with writing the text. If there is no text, and the menu has images attached, we draw a gutter to the left.
Drawing the actual menuitems is much the same as drawing the separators. Drawing more than one line of text and with different formatting makes it a little bit more complicated though. We have to measure the caption and the hint one by one, and then add the heights together. The width is set to be the wider of the two. If a shortcut is specified for the item, the code includes room for it to the right of the caption.
When drawing the menuitems, I have added a nice gradient effect for selected items. The code for drawing gradients is taken from the Jedi Code Library, and I draw it in two steps. First I draw a gradient from light to dark under the caption, and then I draw a new gradient from dark to light where the hint will go. You can see from the code that I use a clip-region to make the selection a rounded rectangle. To know where the first gradient should stop and the second start, we first have to measure the height of the caption again. This is done with a new call to DrawText.
If an imagelist is provided, a gutter is drawn to the left, leaving room for the icons. The icons itself is drawn by the imagelists Draw method.
Unsolved issues :(
This article, together with the sample code, shows how you can spice up your plain old menus into something that looks quite like the new Office style to come. The code provided is far from perfect. The drawing could be optimized by caching pens and brushes, and different visual styles could be encapsulated into ’styleobjects’ so that easy customization could be possible.
The sample code does not handle drawing of checked and default menuitems. It does not handle drawing of the main menubar either. Some developers would prefer to implement all of this in a custom component and they are free to do that if they want to :)