C# Creating Dynamic Menus






3.38/5 (14 votes)
Sep 6, 2003
2 min read

110271

3947
Creating Menus Dymanically in C#.
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 MenuItem
s 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.