65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 创建动态菜单

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.38/5 (14投票s)

2003年9月6日

2分钟阅读

viewsIcon

110670

downloadIcon

3947

在 C# 中动态创建菜单。

Sample Image - DynamicMenus.jpg

引言

本文展示了如何在 C# 中以几个简单的步骤动态创建菜单。它还展示了如何使用 MenuItemOwnerDraw 功能。

背景

几个月前,看到我的一些文章后,有人发邮件给我,请求帮助创建菜单项以将最近使用的文件显示到项目中。该请求是针对 VC++ 中的解决方案。由于我对它非常熟悉,所以我给他发送了必要的源代码,但最近当我(暂时)切换到 C# 时,我遇到了同样的问题,并且在 MSDN 帮助中找不到创建此类菜单的任何参考。我确实找到了一些粗略的信息,我对这些信息进行了一些研究,发现它不是很难,但也不是很容易。但是,当我在 .NET 的 Visual Basic 方面尝试相同的技术时,我没有成功(我现在仍在尝试)。

重要

该项目是在 .NET (7.0 - Visual Studio .NET 的第一个版本) 中开发的,该项目使用以下注册表项来检索并在菜单中显示最近的项目。

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

如果您使用的是 Visual Studio .NET 的更高版本,则可能需要修改上述行以反映您的 .NET 版本;可能类似于这样,然后重新构建应用程序。

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

使用代码

我没有花时间为此目的开发单独的类,因为该技术非常简单,而且我个人鼓励开发人员坚持使用“复制/粘贴/修改”的方法。这样,您学习到的东西比添加其他人制作的类更多,在这些类中,您通过传递一些参数来创建菜单,并且一无所获。您还被迫添加作者的版权声明。

由于项目非常小,因此我在本文中包含了一些重要的源代码部分。

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();
}

添加每个 MenuItem 的函数如下

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;
}

..._MeasureItem(...)..._DrawItem(...) 的代码如下所示。请注意,由于 MenuItem 是在运行时动态创建的,因此无法从 Visual Studio IDE 的属性/事件窗口创建以下事件,而必须手动创建。我已将事件命名为 menuItemDynamicMenu_MeasureItemmenuItemDynamicMenu_DrawItem。您可以随意命名它们,但最好附加 _MeasureItem_DrawItem 以便于识别。每个这些事件的参数必须如下。如果您省略 _MeasureItem 事件,则您的菜单大小将为 10x2。

..._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;
}

_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);
   }
}

历史

  • First version.
© . All rights reserved.