Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#
Article

Customizable MainMenu on C#

Rate me:
Please Sign up or sign in to vote.
3.08/5 (9 votes)
23 Dec 20043 min read 127.7K   845   34   25
An article on custom main menus and owner drawing.

Sample Image

Introduction

This article describes a way to customize the main menu of our application in C#. It can be completely and easily changed. This is my first article and I hope that it may be of some use.

Background

I've made this article from a code that I have found in this page, which I strongly recommend reading. Actually, the code is very similar, the main difference resides in the fact that I worked around a couple of limitations in the original code. The first limitation regards the fact that the code presented in the original article sets the size of the menu items which implies that they are not dynamic. The second limitation resides in the fact that when you draw your own menu, all the special characters like the separator "-" character (creates a line that divides two items) are not recognized. There is another limitation. You can't change the menus' color, only the items'!!! The rest of the menu (where you haven't created items) won't change its color. Try to create the menu according to the original article, but make the menu blue and you'll see what I'm saying.

Using the code

The idea is to create a regular main menu and then define the OwnerDraw variable so that we have to do the drawing and then define the DrawItem() and MeasureItem() functions to draw our menu. We will have to do this to every item in our menu. Luckily for us, we will only need to create two DrawItem() and MeasureItem() functions, to do the job. One takes care of the main menu items and the other does the dropdown menus when we hit a main menu item.

To take care of the color problem, we have to crate an extra main menu item which will be disabled, and then for its drawing, we will use a special function.

  1. Create your menu as always (using MainMenu in the toolbox, or whatever) and make an extra main menu item.

    Creation of the menu

  2. In the Items proprieties, define the OwnerDraw parameter to True, and in the "extra" item, set the Enable parameter to False.
  3. Add this code to your application:
    C#
    //Some colors I'll use.
    private Color grad1=Color.FromArgb(165,194,245);
    private Color grad2=Color.FromArgb(209,232,255);
    private Color grad3=Color.FromArgb(255,210,151);
    private Color grad4=Color.FromArgb(241,241,231);
    private Color linhalight=Color.FromArgb(169,184,215);
    private Color linha=Color.FromArgb(10,47,115);
    
    //
    //This is where we draw the main menu items.
    //
    private void OnDrawItem(object sender, 
                 System.Windows.Forms.DrawItemEventArgs e)
    {
        //rectangle that contains the dimentions of the item.
        Rectangle rc = new Rectangle(e.Bounds.X , 
                  e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
    
        //draws the button.
        e.Graphics.FillRectangle(new SolidBrush(grad1),rc);
    
        // Cast the sender to MenuItem so you can access text property.
        MenuItem customItem = (MenuItem) sender;
    
        // Create a Brush and a Font to draw the item's text.
        System.Drawing.Brush aBrush = System.Drawing.Brushes.Black;
        Font aFont = new Font("Microsoft Sans Serif", (float)8.25, 
                FontStyle.Regular, GraphicsUnit.Point);
        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
        rc.Y+=3;
        e.Graphics.DrawString(customItem.Text, aFont, aBrush, rc , sf );
        rc.Y-=3;
    
        //if the mouse is hover the item it changes the looks.
        if (e.State==(DrawItemState.NoAccelerator | DrawItemState.HotLight))
        {
            e.Graphics.FillRectangle(new LinearGradientBrush(rc,grad4, 
                             grad3,LinearGradientMode.Vertical) , rc);
            rc.Y+=3;
            e.Graphics.DrawString( customItem.Text , aFont , 
                       new SolidBrush(Color.Black), rc ,sf);
            rc.Y-=3;
            rc.Width--;
            e.Graphics.DrawRectangle(new Pen(Color.Blue,1), rc );
            rc.Width++;
        }
        else
        {
            //if the mouse hits the item it changes the looks.
            if ( e.State==(DrawItemState.NoAccelerator | DrawItemState.Selected))  
            {
                e.Graphics.FillRectangle(new LinearGradientBrush(rc, 
                           grad2,grad1,LinearGradientMode.Vertical) , rc);
                rc.Y+=3;
                e.Graphics.DrawString( customItem.Text , 
                           aFont , new SolidBrush(Color.Black), rc ,sf);
                rc.Y-=3;
                rc.Width--;
                e.Graphics.DrawRectangle(new Pen(new SolidBrush(linha)), rc );
                rc.Width++;
                e.Graphics.DrawLine(new Pen(linhalight,1),rc.X , 
                                    rc.Bottom, rc.Right, rc.Bottom);
            }
            else
                e.Graphics.DrawLine(new Pen(Color.White,1),rc.X , 
                                    rc.Bottom, rc.Right, rc.Bottom);
        }
        //draws the the focus rectangle.
        e.DrawFocusRectangle();
    }
    
    //
    //This is where we set the main menu items size.
    //
    private void OnMeasureItem(object sender, 
                 System.Windows.Forms.MeasureItemEventArgs e)
    {
        // Cast the sender to MenuItem so you can access text property.
        MenuItem customItem = (MenuItem) sender;
    
        // Create a Brush and a Font to draw the item's text.
        Font aFont = new Font("Microsoft Sans Serif", (float)8.25, 
                    FontStyle.Regular, GraphicsUnit.Point);
    
        //Gets the size of the drawn string.
        SizeF stringSize = e.Graphics.MeasureString(customItem.Text, aFont);
    
        //Sets the size of the menu item.
        e.ItemWidth=Convert.ToInt32(stringSize.Width);
        e.ItemHeight=20;
    }
    
    //
    //This is where we draw the dropdown menu items.
    //
    private void OnDrawItemSec(object sender, 
                 System.Windows.Forms.DrawItemEventArgs e)
    {
        //rectangle that contains the dimentions of the item.
        Rectangle rc = new Rectangle(e.Bounds.X , 
                       e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
    
        //draws the button.
        e.Graphics.FillRectangle(new SolidBrush(Color.White),rc);
    
        // Cast the sender to MenuItem so you can access text property.
        MenuItem customItem = (MenuItem) sender;
    
        // Create a Brush and a Font to draw the item's text.
        System.Drawing.Brush aBrush = System.Drawing.Brushes.Black;
        Font aFont = new Font("Microsoft Sans Serif", (float)8.25, 
                    FontStyle.Regular, GraphicsUnit.Point);
        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
    
        //if the text is the separator caracter ('-') draws the separator.
        if(customItem.Text=="-")
            e.Graphics.DrawLine(new Pen(Color.DarkGray,1), 
                        rc.X,rc.Y+2,rc.X+rc.Right,rc.Y+2);
        else
        {
            rc.Y+=3;
            e.Graphics.DrawString(customItem.Text, aFont, aBrush, rc , sf );
            rc.Y-=3;
        }
    
        //if the mouse is hover the item it changes the looks.
        if ( e.State==(DrawItemState.NoAccelerator | DrawItemState.Selected))  
        {
            e.Graphics.FillRectangle(new SolidBrush(grad1) , rc);
            rc.Y+=3;
            e.Graphics.DrawString( customItem.Text , aFont , 
                                   new SolidBrush(Color.Black), rc ,sf);
            rc.Y-=3;
            rc.Height--;
            rc.Width--;
            e.Graphics.DrawRectangle(new Pen(new SolidBrush(linha)), rc );
            rc.Height++;
            rc.Width++;
        }
        e.DrawFocusRectangle();
    }
    
    //
    //This is where we set the dropdown menu items.
    //
    private void OnMeasureItemSec(object sender, 
                 System.Windows.Forms.MeasureItemEventArgs e)
    {
        // Cast the sender to MenuItem so you can access text property.
        MenuItem customItem = (MenuItem) sender;
    
        // Create a Brush and a Font to draw the item's text.
        Font aFont = new Font("Microsoft Sans Serif", (float)8.25, 
        FontStyle.Regular, GraphicsUnit.Point);
    
        //Gets the size of the drawn string.
        SizeF stringSize = e.Graphics.MeasureString(customItem.Text, aFont);
        e.ItemWidth=Convert.ToInt32(stringSize.Width);
    
        //Sets the size of the menu item.(regards the separator case)
        if(customItem.Text=="-")
            e.ItemHeight=5;
        else
            e.ItemHeight=20;
    }
    
    //
    //This is where we draw the extra item to color the remaining menu.
    //
    private void OnDrawExtra(object sender, 
                 System.Windows.Forms.DrawItemEventArgs e)
    {
        Rectangle area = new Rectangle(e.Bounds.X , e.Bounds.Y, 
                         this.ClientRectangle.Right-
                         (e.Bounds.X-this.ClientRectangle.X-4), 19);
        e.Graphics.FillRectangle(new SolidBrush(grad1),area);
    }
  4. Now for the main menu items, define their events parameters and set the DrawItem as OnDrawItem (you've added above) and MeasureItem as OnMeasureItem. Do the same for the drop down items but this time using the OnDrawItemSec and the OnMeasureItemSec. For the extra item, we set the DrawItem event to do the OnDrawExtra.

Compile and see your menu's new look.

Limitations

This code is an improvement from the code on the original article, but it is still far from perfection. For example, the '&' special character problem. Like the '-' problem, the '&' instead of underlining the key character of our menu item, is drawn.

Example:

Item -> &View

Shows: &View

Instead of: View.

Another problem is that 'horrible' white line under the menu.

Hope you've liked the control.

History

23-12-2004

  • Version 1.

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
Software Developer
Portugal Portugal
I'm that guy that falls for the impossible...

Comments and Discussions

 
QuestionHow to draw menuitem's Shortcut? Pin
cq_wxy8-Feb-06 19:08
cq_wxy8-Feb-06 19:08 
Generali need to change the menu hiegt Pin
shabonaa29-Oct-05 7:38
shabonaa29-Oct-05 7:38 
GeneralGone - the 'horrible' white line Pin
Mark Johnson29-Jul-05 1:29
Mark Johnson29-Jul-05 1:29 
GeneralRe: Gone - the 'horrible' white line Pin
Libor Tinka18-Oct-05 2:00
Libor Tinka18-Oct-05 2:00 
GeneralRe: Gone - the 'horrible' white line Pin
psorr22-Nov-05 12:48
psorr22-Nov-05 12:48 
Generalwhole menu not colored on maximized Pin
arfeena7-Jul-05 11:40
arfeena7-Jul-05 11:40 
GeneralRe: whole menu not colored on maximized Pin
João Martins7-Jul-05 13:51
João Martins7-Jul-05 13:51 
GeneralRe: whole menu not colored on maximized Pin
sergio.cruz.r10-Feb-06 2:51
sergio.cruz.r10-Feb-06 2:51 
Dim area As Rectangle = New Rectangle(e.Bounds.X, e.Bounds.Y, (Me.ClientRectangle.Right _
- (e.Bounds.X _
- (Me.ClientRectangle.X + 3))), 19)

On OnDrawExtra Replace the code of example with this

Hope it helps

QuestionO.K. code. font hardcode ?? Pin
mlavenne1-Apr-05 6:28
mlavenne1-Apr-05 6:28 
GeneralThe white line problem Pin
jonnospam27-Feb-05 0:14
jonnospam27-Feb-05 0:14 
GeneralRe: The white line problem Pin
Radu Cosoveanu24-Aug-05 7:35
Radu Cosoveanu24-Aug-05 7:35 
QuestionHow to convert to VB.net Pin
David M J29-Dec-04 12:29
David M J29-Dec-04 12:29 
AnswerRe: How to convert to VB.net Pin
David Nissimoff29-Dec-04 14:00
David Nissimoff29-Dec-04 14:00 
GeneralRe: How to convert to VB.net Pin
David M J29-Dec-04 20:41
David M J29-Dec-04 20:41 
GeneralRe: How to convert to VB.net Pin
David Nissimoff30-Dec-04 2:19
David Nissimoff30-Dec-04 2:19 
GeneralRe: How to convert to VB.net Pin
David M J30-Dec-04 12:14
David M J30-Dec-04 12:14 
GeneralRe: How to convert to VB.net Pin
João Martins30-Dec-04 14:32
João Martins30-Dec-04 14:32 
GeneralRe: How to convert to VB.net Pin
chelonian22-Feb-05 2:36
chelonian22-Feb-05 2:36 
GeneralRe: How to convert to VB.net Pin
LordRhys4-Jan-05 4:40
LordRhys4-Jan-05 4:40 
QuestionHow can I? Pin
Member 152967024-Dec-04 20:56
Member 152967024-Dec-04 20:56 
AnswerRe: How can I? Pin
João Martins25-Dec-04 3:34
João Martins25-Dec-04 3:34 
GeneralRe: How can I? Pin
Member 152967025-Dec-04 10:36
Member 152967025-Dec-04 10:36 
GeneralTo draw the hot key underlined Pin
Eric Woodruff24-Dec-04 9:50
professionalEric Woodruff24-Dec-04 9:50 
GeneralRe: To draw the hot key underlined Pin
João Martins25-Dec-04 3:35
João Martins25-Dec-04 3:35 
GeneralRe: To draw the hot key underlined Pin
Yurich12-Jan-05 11:17
Yurich12-Jan-05 11:17 

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.