Owner Draw Menus in C#


Windows applications, which focuses mainly on its GUI aspects, can make use of the ownerdraw properties of the various forms components. Once such component that allows us to define our own drawing and painting of items is the Menu component of Windows Forms.

This article will explain to you how we can draw our own menu items with our own fonts, pictures, background color and other graphics objects.

Step 1 : Create a simple windows form application

Click File - > New -> Project - >

Create new application OD_Menu.

In the designer view for the default form created , add a new "Main Menu" component from the Windows Forms Toolbox.

Using the designer , create menu and menu items.



We need to ownerdraw not only the top level menu items, but also all the submenu items.

For every menu item select the "Properties" and set the OwnerDraw option to 'True".

For every owerdraw menu item the application will call 2 functions..

DrawItem : This function will do the actual painting and drawing of the intended menu item.

Measure Item : This function is called to set the height / width of the menu item.

We need to add these 2 events from the "Events" tab of the menu properties.

Select the properties for the "File" menu item

Click on the Events tab. Double Click the Draw Item option. It will add a default handler to handle the drawing of the "File" menu item.

private void menuItem1_DrawItem(object sender,System.Windows.Forms.DrawItemEventArgs e)

Double Click the MeasureItem otpion. It will add a
default handler.

private void menuItem1_MeasureItem(object sender,System.Windows.Forms.MeasureItemEventArgs e)

Now we need to add the sametype of DrawItem and MeasureItem for the sub menu items. We are creating a different event handler for the subitems , since we need to do some more fancy stuff with the sub items like drawing icons or bitmaps.

Select the first subitem "Open" and add the same 2 events for this item

private void menuItem1_MeasureItem(object sender,System.Windows.Forms.MeasureItemEventArgs e)

private void menuItem2_DrawItem(object sender,System.Windows.Forms.DrawItemEventArgs e)

Important: Since we need to have a DrawItem and MeasureItem for each and every item , it does not make sense to duplicate the events for every menu item. We can have the mainmenu items ( File / Options / Help ) be handle via a common event handler and we can have the submenu items like ( Open /Close ... About ) be routed through another common event handler.

Here is how to do this....

For every other main menu item like "Options and Help". Select the item in designer.Click proerties.

Click the Events tab.
Click the listbox next to the DrawItem and select the menuItem1_DrawItem option from the list.
Click the listbox next to the MeasureItem and select the menuItem1_MeasureItem option from the list.

For every other sub menu items like "Close / Exit / Security / Network / About".Click proerties.

Click the Events tab.
Click the listbox next to the DrawItem and select the menuItem2_DrawItem option from the list.
Click the listbox next to the MeasureItem and select the menuItem2_MeasureItem option from the list.

We are all set to add our own code the handle the drawing and paiting of the TopLevel Menu Items and the SubItems.

Code for the Sub menu Items

private void menuItem1_DrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
Rectangle rc = new Rectangle(e.Bounds.X+1 , e.Bounds.Y+1,e.Bounds.Width-5, e.Bounds.Height-1);
e.Graphics.FillRectangle(
new SolidBrush(Color.LightGray) , rc);
MenuItem s = (MenuItem)sender ;
string s1 = s.Text ;
StringFormat sf =
new StringFormat();
sf.Alignment = StringAlignment.Center ;
e.Graphics.DrawString(s1 ,
new Font("Ariel" ,10), new SolidBrush(Color.Black) , rc ,sf );
Console.WriteLine(e.State.ToString());
if ( e.State == (DrawItemState.NoAccelerator | DrawItemState.Selected) ||
e.State == ( DrawItemState.NoAccelerator | DrawItemState.HotLight) )
{
e.Graphics.FillRectangle(
new SolidBrush(Color.CornflowerBlue) , rc);
e.Graphics.DrawString( s1 ,
new Font("Veranda" , 10) ,new SolidBrush(Color.Black), rc ,sf);
e.Graphics.DrawRectangle(
new Pen(new SolidBrush(Color.Black)), rc );
}
e.DrawFocusRectangle();
e.Graphics.DrawRectangle(
new Pen(new SolidBrush(Color.Black), 2 ), rc );
}
private void menuItem1_MeasureItem(object sender,System.Windows.Forms.MeasureItemEventArgs e)
{
e.ItemWidth = 75 ;
e.ItemHeight = 25 ;
}

Code for the Sub menu Items

private void menuItem2_DrawItem(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.LightGray) , rc);
MenuItem s = (MenuItem)sender ;
string s1 = s.Text ;
StringFormat sf =
new StringFormat();
sf.Alignment = StringAlignment.Far ;
sf.LineAlignment = StringAlignment.Center;
Rectangle rcText = rc ;
rcText.Width-=5 ;
e.Graphics.DrawString(s1 ,
new Font("Veranda" ,10),new SolidBrush(Color.Blue) , rcText, sf );
e.Graphics.DrawRectangle(
new Pen(new SolidBrush(Color.LightGray)), rc );
if ( e.State == ( DrawItemState.NoAccelerator | DrawItemState.Selected))
{
e.Graphics.FillRectangle(
new SolidBrush(Color.CornflowerBlue) , rc);
e.Graphics.DrawString( s1 ,
new Font("Veranda" , 10 , FontStyle.Bold | FontStyle.Underline
) ,
new SolidBrush(Color.Yellow), rcText,sf);
e.Graphics.DrawRectangle(
new Pen(new SolidBrush(Color.Black)), rc );
e.DrawFocusRectangle();
}
Image useImage =
null ;
if ( s1 == "Open" )
{
useImage = img_fileopen;
}
if ( s1 == "Close" )
{
useImage = img_close;
}
if ( s1 == "Exit" )
{
useImage = img_exit;
}
if ( s1 == "Security" )
{
useImage = img_security;
}
if ( s1 == "Network" )
{
useImage = img_network;
}
if ( s1 == "About" )
{
useImage = img_about;
}
if ( useImage != null )
{
SizeF sz = useImage.PhysicalDimension;
e.Graphics.DrawImage(useImage, e.Bounds.X+5,( e.Bounds.Bottom + e.Bounds.Top ) /2 - sz.Height/2);
}
}
private void menuItem2_MeasureItem(object sender,System.Windows.Forms.MeasureItemEventArgs e)
{
e.ItemWidth = 75 ;
e.ItemHeight = 25 ;
}

Compile and execute the application.