Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# Creating Dynamic Menus

0.00/5 (No votes)
5 Sep 2003 1  
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.

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