65.9K
CodeProject is changing. Read more.
Home

C# Creating Dynamic Menus

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.38/5 (14 votes)

Sep 6, 2003

2 min read

viewsIcon

110271

downloadIcon

3947

Creating Menus Dymanically in C#.

Sample Image - DynamicMenus.jpg

Introduction

This article shows how to create menus dynamically in C#, in a few easy steps. It also shows how to use the OwnerDraw feature of MenuItem(s).

Background

Some months back, seeing some of my articles, someone sent me an E-mail message asking for help on creating menu items to display Most Recent Used files to a project. This request was for a solution in VC++. Since I was quite familiar with it, I sent him the necessary source, but recently when I switched to C# (temporarily), I came across the same problem and could not find any reference to creating such menus in the MSDN Help. I did find some sketchy info in this regard, I worked on the bits of info and found that it is not very difficult, and not very easy either. But however when I tried this same technique in Visual Basic side of .NET, I did not succeed (I am at this point of time still trying).

Important

This project was developed in .NET (7.0 - first version of Visual Studio .NET) and the project uses the following registry key to retrieve and display the most recent project, in the menu.

RegistryKey regKey = 
     Registry.CurrentUser.OpenSubKey
     ("Software\\Microsoft\\VisualStudio\\7.0\\ProjectMRUList");

If you are using a later version of Visual Studio .NET, you may have to modify the above line to reflect your version of .NET; maybe something like this, and re-build the application.

RegistryKey regKey = 
     Registry.CurrentUser.OpenSubKey
     ("Software\\Microsoft\\VisualStudio\\7.1\\ProjectMRUList");

Using the code

I have not spent time trying to develop a separate class for this purpose, since the technique is quite simple, and personally I would encourage developers to stick to a method of "Copy / Paste / Modify". Thus you learn more then adding classes made by others, wherein you create menus by passing a few parameters, and learn nothing in the bargain. You are also forced to add the copyright notice of the author.

Since the project is very small I have included some of the important parts of the source in this article.

public void UpdateMenu()
{

// retreive names of all recent project
// from the system registry
   RegistryKey regKey = Registry.CurrentUser.OpenSubKey
       ("Software\\Microsoft\\VisualStudio\\7.0\\ProjectMRUList");
   if (regKey == null)
   {
       // such key does not exist
       return;
   }

   MenuItem mnu = new MenuItem();                
   String[] filename = regKey.GetValueNames(); 
   IEnumerator iEnum = filename.GetEnumerator();

   // Iterate through all items
   while(iEnum.MoveNext())
   {
       String val = (String)iEnum.Current;
       String data = (String)regKey.GetValue(val);
       MenuItem mi = AddMenuItem(data);
       mnu.MenuItems.Add(mi);
   }
   menuItemRecentProjects.MergeMenu(mnu);
   regKey.Close();
}

Function to add each MenuItem is as below:

private MenuItem AddMenuItem(String title)
{
   // create a new menuitem
   MenuItem mi = new MenuItem(title);
   // set as Delegate for the Click Event of this MenuItem
   mi.Click +=  new 
     System.EventHandler(this.menuItemDynamicMenu_Click);

   // Ownerdraw is needed only if you want to
   // add Icons and custom highlighting etc
   mi.OwnerDraw = true;
   // ..._MeasureItem, ..._DrawItem Virtual function are called
   // by the system to render the items individually
   // every time a visual aspect changes
   mi.DrawItem +=  new 
     System.Windows.Forms.DrawItemEventHandler
     (this.menuItemDynamicMenu_DrawItem ); 
   mi.MeasureItem += new 
     System.Windows.Forms.MeasureItemEventHandler
     (this.menuItemDynamicMenu_MeasureItem);
   // ..._DrawItem and ..._MeasureItem EventHandler Delegates 
   // for each MenuItem are created above
   return mi;
}

Code for ..._MeasureItem(...) and ..._DrawItem(...) is shown below. Note, since the MenuItems are created dynamically at run-time, the following events cannot be created from the Properties/Events window of the Visual Studio IDE, but rather have to be created manually. I have named the events menuItemDynamicMenu_MeasureItem and menuItemDynamicMenu_DrawItem. You can possibly name them anything you prefer, but it is better to append _MeasureItem and _DrawItem to recognize it easily. Parameters for each of these events must be as below. If you leave out the _MeasureItem event your menu will be 10x2 in size.

..._MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
..._DrawItem(object sender, 
private void menuItemDynamicMenu_MeasureItem(object sender, 
       System.Windows.Forms.MeasureItemEventArgs e)
{
   // retreive the MenuItem in question
   // note : ..._MeasureItem is called before ..._DrawItem
   MenuItem item = (MenuItem)sender;
   // we set the height of each item to 20
   // remember the icons are 16x16
   e.ItemHeight = 20;
   SizeF stringSize = new SizeF();
   stringSize = e.Graphics.MeasureString(item.Text, this.Font);
   // set the width of the item based on the 
   // length of the prompt
   e.ItemWidth = (int)stringSize.Width;
}

Code for _DrawItem(...).

private void menuItemDynamicMenu_DrawItem(object sender, 
             System.Windows.Forms.DrawItemEventArgs e)
{
   // unfortunately only 2 states are passed to this function
   // NoAccelerator
   // Selected, NoAccelerator

   SolidBrush brush;
   if(e.State.Equals(DrawItemState.Selected | 
                   DrawItemState.NoAccelerator ))
   {
      // Item is Selected highlight the Item
      Pen blackPen = new Pen(Color.Black, 1);
      e.Graphics.DrawRectangle(blackPen, e.Bounds); 
      brush = new SolidBrush(Color.FromArgb(40, 10, 10, 255));
      // create a highlight rectangle
      e.Graphics.FillRectangle(brush, e.Bounds); 
      return;
   }
            
   if(e.State.Equals(DrawItemState.NoAccelerator))
   {
      // Item is not selected, remove highlight rectangle
      // created above
      Pen whitePen = new Pen(Color.White, 1);
      e.Graphics.DrawRectangle(whitePen, e.Bounds) ;
      SolidBrush brushErase = new SolidBrush(Color.White );
      e.Graphics.FillRectangle(brushErase, e.Bounds); 

      MenuItem item = (MenuItem)sender;
            
      // get the MenuItem's Rectangle
      Rectangle rect = e.Bounds;

      SolidBrush drawBrush = new SolidBrush(Color.Black);
      RectangleF drawRect = new RectangleF( e.Bounds.X+20, 
            e.Bounds .Y, e.Bounds.Width , e.Bounds .Height);
      brush = new SolidBrush(Color.FromArgb(236, 233,216));
      // draw a transparent rectangle over the Image
      // else the Image will not be visible
      e.Graphics.FillRectangle(brush, 
            rect.Left, rect.Top, 18, rect.Bottom);

      // Draw MenuItem Text , you can use some funcky 
      // Font for this purpose
      e.Graphics.DrawString(item.Text , e.Font, 
                            drawBrush, drawRect );

      // Render the Image (Icon)
      // i have used a ImageList 
      imageList1.Draw(e.Graphics, rect.Left+1, rect.Top ,14,14, 0);
   }
}

History

  • First version.