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.
- Create your menu as always (using
MainMenu
in the toolbox, or whatever) and make an extra main menu item.
- In the Items proprieties, define the
OwnerDraw
parameter to True, and in the "extra" item, set the Enable
parameter to False.
- Add this code to your application:
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);
private void OnDrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
Rectangle rc = new Rectangle(e.Bounds.X ,
e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(grad1),rc);
MenuItem customItem = (MenuItem) sender;
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 (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 ( 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);
}
e.DrawFocusRectangle();
}
private void OnMeasureItem(object sender,
System.Windows.Forms.MeasureItemEventArgs e)
{
MenuItem customItem = (MenuItem) sender;
Font aFont = new Font("Microsoft Sans Serif", (float)8.25,
FontStyle.Regular, GraphicsUnit.Point);
SizeF stringSize = e.Graphics.MeasureString(customItem.Text, aFont);
e.ItemWidth=Convert.ToInt32(stringSize.Width);
e.ItemHeight=20;
}
private void OnDrawItemSec(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
Rectangle rc = new Rectangle(e.Bounds.X ,
e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.White),rc);
MenuItem customItem = (MenuItem) sender;
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(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 ( 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();
}
private void OnMeasureItemSec(object sender,
System.Windows.Forms.MeasureItemEventArgs e)
{
MenuItem customItem = (MenuItem) sender;
Font aFont = new Font("Microsoft Sans Serif", (float)8.25,
FontStyle.Regular, GraphicsUnit.Point);
SizeF stringSize = e.Graphics.MeasureString(customItem.Text, aFont);
e.ItemWidth=Convert.ToInt32(stringSize.Width);
if(customItem.Text=="-")
e.ItemHeight=5;
else
e.ItemHeight=20;
}
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);
}
- 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