![]() |
Desktop Development »
Menus »
General
Intermediate
Menu Images using C# and IExtenderProvider - a better mousetrap!By Chris BeckettHow to extend the standard menus to support icons using IExtender in C#. |
C#.NET 1.0, WinXP, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

If you are reading this article, you have probably noticed that Microsoft
failed to provide a decent menu control in Visual Studio.NET. The standard .NET
Framework MainMenu and MenuItem controls are as basic as
they get.
A long search through some other articles on how to extend the features of the
.NET Framework menus, left me unsatisfied; either they used Interop and Win32 API
calls which I am trying to avoid and/or, they subclass the MenuItem which means
foregoing the menu designer in Visual Studio.NET, and hand-tooling your menu
implementation. I also found that either the code was far too simple (and
incomplete), or too complex and integrated into a large control suite that made
it difficult to pull-out just the menu functionality.
The source project included with this submission uses IExtenderProvider to
create a bridge between an ImageList control containing menu icons,
and standard MenuItem controls. The benefit is that, you can continue to design
your menus using the menu designer in Visual Studio.NET, and simply extend them to support
a MenuImage property that also takes care of the work of owner drawing your menu
items. All you need is a few drag-and-drop operations, and few property set
values and you will have fully-functional graphical menus with no-coding required.
Using the MenuImage extender is as simple as it get:
ImageList control to your form, and populate the control
with your menu icons. It is recommended that you use 16x16 transparent icons.
Although the MenuImage extender will support any image supported by the
ImageList, my implementation does not make bitmaps transparent.
MenuImage extender to your form. Open the properties window,
and select your ImageList control instance from the ImageList property
drop-down menu. This hooks your ImageList into the extender. Next, select
your menu items and note a new property - MenuImage. Enter the numeric
index of the image item to associate with this menu.
That's it. No coding required. When you run your application, the MenuImage
extender will retrieve the indicated image from your ImageList, and owner draw
the menu as seen above.
By default, .NET Framework menus provide no image property on the MenuItem
class. To add one requires defining your own drawing and painting code and
basically rendering the menus yourself from scratch.
To indicate that you
are going to draw your own menu items, the MenuItem class provides an OwnerDraw property. By default, this property is false. To
custom draw your menus, set the OwnerDraw property to true.
NOTE: the MenuImage extender does this for you by default. If OwnerDraw is
true, then the MenuItem class raises two events that can be used to draw the
menu.
Special note: you do not need to implement a subclass of the control to have access to these events - this is what makes implementing this functionality as an extender possible.
The MeasureItem event is used to calculate the height and width of the
canvas required for the control. This event is raised prior to DrawItem. The
primary activity is to set the ItemHeight and ItemWidth properties to the
correct size.
private void OnMeasureItem( Object sender, MeasureItemEventArgs e )
{
// retrieve the image list index from hash table
MenuItem menuItem = (MenuItem) sender ;
// create a menu helper to actually do the menu drawing/painting functions
MenuHelper menuHelper = new MenuHelper( menuItem, e.Graphics, _imageList ) ;
// calculate the menu height and width
e.ItemHeight = menuHelper.CalcHeight() ;
e.ItemWidth = menuHelper.CalcWidth() ;
}
The DrawItem event is used to actually perform the drawing. The event
argument provide the state (selected or not), the bounds of the canvas, and
even provide a graphics object to do the painting.
private void OnDrawItem( Object sender, DrawItemEventArgs e )
{
// derive the MenuItem object, and create the MenuHelper
MenuItem menuItem = (MenuItem) sender ;
MenuHelper menuHelper = new MenuHelper( menuItem, e.Graphics, _imageList ) ;
// draw the menu background
bool menuSelected = (e.State & DrawItemState.Selected) > 0 ;
menuHelper.DrawBackground( e.Bounds, menuSelected ) ;
// if the menu is a seperator, then draw it otherwise, draw a normal menu item
if ( menuHelper.IsSeperator() == true )
menuHelper.DrawSeperator( e.Bounds ) ;
else
{
int imageIndex = this.GetMenuImageIndex( sender ) ;
menuHelper.DrawMenu( e.Bounds, menuSelected, imageIndex ) ;
}
}
These are the basics behind owner-drawn controls in general. To keep the main
extender class code simple, I encapsulated the actual menu drawing and painting
features in a separate MenuHelper class. More details on the actual
implementation of the drawing and painting can be found by reviewing the sample
project source code.
Since adding an image requires changing the default offsets for menu text, you cannot mix and match owner-drawn menu items with non-owner drawn menu items. It's an all or nothing deal. Part of the complexity associated with this type of code is related to having to handle separators, non-graphical menus, menu shortcuts, sub-menus, etc., selected vs. non-selected menu items, and enabled and disabled states.
The code does not directly call any Interop or Win32 API functionality. In fact, once I started digging, I found that .NET provides a lot of functionality that allowed me to keep my code footprint relatively small - it is just seeded around a lot of different classes. A special thanks goes to the various authors noted in my acknowledgements. Many of the hidden tricks I have used were pulled from their various articles.
I developed these extensions to support the standard Windows style menu design versus the new XP/Office style menus for a number of reasons:
MenuStyle property that allows the user to select either standard or XP
style menus in future revisions.I drew from a number of different articles by various authors. I would like to acknowledge their indirect contributions as follows:
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 24 Nov 2002 Editor: Smitha Vijayan |
Copyright 2002 by Chris Beckett Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |