Create a Dynamic Menu Using C#

Create a Dynamic Menu Using C#

Introduction

There have been articles on the technique of creating MRU (Most Recently Used) file menus in Visual Studio environment, for example http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c15571. In my recent project, I need not only to create a MRU file menu, but also other menus that are dynamically created. For example, a user-customizable tools menu, help menu, and so forth. This article generalizes the way of creating a MRU menu to introduce the concept of a Dynamic Menu, which is created dynamically at application execution time.

One thing in common in the menus my application needs to create is that each dynamic menu item is associated with data. In the case of a MRU file menu item, it is the file path to be opened, and in the case of a tools menu item, it is the path of an application to be invoked. When it comes to a user-customizable help menu, it is a document file of various types to be displayed. In all these cases, the data associated with a menu item is of string type. It does not prevent you to make it an object to be more general. The MenuItem class in System.Windows.Forms namespace has the object type property Tag that can be used to store user data. You can utilize this placeholder to store the data of dynamic menu items, but I prefer to derive a class from the MenuItem item class. This allows you to further expand this class in the future when needed, and also it makes the code easy to understand. In my example code, the derived class is named DynamicMenuItem. To simplify the code, I make this data member string type, which meets the needs of MRU file menu and tools menu.

To add dynamic menu items to a menu list, a dynamic menu manager class is designed. The manager class controls the type of dynamic menu list and the way to add a menu item to the menu list that I will explain in detail.

The Dynamic Menu Item Class

As has been discussed, the dynamic menu item class is derived from MenuItem class.

public class DynamicMenuItem : MenuItem
{
   private string _data;

   // Dynamic menus information, i.e. menu text and menu data are
   // normally retrieved from some source like a database or
   // Registry. Putting them into an array of DynamicMenuItemTextData
   // struct will make it convenient to prepare for creating a group
   // of dynamic menu items through the dynamic menu manager.
   public struct DynamicMenuItemTextData
   {
      string _menuText;
      string _menuData;

      public string MenuText
      {
         get { return _menuText; }
         set { _menuText = value; }
      }
      public string MenuData
      {
         get { return _menuData; }
         set { _menuData = value; }
      }
      public DynamicMenuItemTextData(string menuText,
                                     string menuData)
      {
         _menuText = menuText;
         _menuData = menuData;
      }
   }

   public string Data
   {
      get { return _data; }
      set { _data = value; }
   }

   public DynamicMenuItem(string text, string data,
                          System.EventHandler eventHandler)
      : base(text)
   {
      _data = data;
      // Add menu item clicked event handler when it is created.
      this.Click += new System.EventHandler(eventHandler);
   }
}

Anchor Menu Items, Dynamic Menu Types, and Dynamic Menu Manager

An anchor menu item is an menu item added at menu design time. The purpose of an anchor is to position the dynamic menu item list. The dynamic menu item list may be at the same level of the anchor, which I call the inline dynamic menu item list. The list is immediately below the anchor. The dynamic menu item list also can be a submenu of the anchor. In the case of an inline menu list, the anchor should be invisible. The separator immediately above it or below it should also be invisible until there is one dynamic menu item added. If the menu list forms a submenu of the anchor, the anchor is visible but is disabled until there is one dynamic menu item added. The separator is always visible in this case. The type of dynamic menu type and the visibility of the anchor is controlled by DynamicMenuMgr. When a new dynamic menu item is added to the menu list, it can be put on top of the list or be appended to the list. The manner of adding a new menu item is also controlled by DynamicMenuMgr.

When a new menu item is added, DynamicMenuMgr is responsible for registering the menu item click event handler. All the dynamic menu items share a common event handler. In my demo code, the event handler displays the data stored in the menu item being clicked in a message box.

private void MenuItemClick(object sender, EventArgs e)
{
   DynamicMenuItem item = (DynamicMenuItem)sender;
   MessageBox.Show(item.Data);
}

Create a Dynamic Menu Using C#

Following is the code for the DynamicMenuMgr class. I have kept it as simple as possible as long as it meets the needs of my demo project and it illustrates the basic idea. You can write a class derived from DynamicMenuMgr to extend the capabilities. For example, you can add code to retrieve menu text and data from a source such as the system Registry, or a database table or XML file. You also need to rewrite the dynamic menu item click event handler to do real things, such as open a file.

public class DynamicMenuMgr
{
   MenuItem _anchor;
   MenuItem _separator;
   DynamicMenuType _dynamicMenuType;
   ItemInsertMode _itemInsertMode;
   int _maxItems;
   int _itemCount;

   public enum ItemInsertMode
   {
      Prepend,
      Append
   };

   public enum DynamicMenuType
   {
      Inline,
      Submenu
   };

   // Constructor.
   public DynamicMenuMgr(MenuItem anchor, MenuItem separator,
      DynamicMenuType dynamicMenuType,
      ItemInsertMode itemInsertMode, int maxItems)
   {
      _anchor = anchor;
      _separator = separator;
      _itemInsertMode = itemInsertMode;
      _dynamicMenuType = dynamicMenuType;
      _maxItems = maxItems > 0? maxItems : 4;
      _itemCount = 0;
      // DynamicMenuType is inline.
      if (_dynamicMenuType == DynamicMenuType.Inline)
      {
         // Hide _anchor and _separator if dynamic menus will be inline.
         _anchor.Visible = false;
         if (_separator != null) _separator.Visible = false;
      }
      // DynamicMenuType is submenu.
      else
      {
         // Make _anchor visible as it will be the parent menu item
         // of dynamic menu items in a submenu.
         _anchor.Visible = true;
         // Disable _anchor as there is no menu item in the submenu
         // initially.
         _anchor.Enabled = false;
         // _separator should be visible if there is one.
         if (_separator != null) _separator.Visible = true;
      }
   }
   // Another constructor with maxItems defaults to 4.
   public DynamicMenuMgr(MenuItem anchor, MenuItem separator, 
      DynamicMenuType DynamicMenuType, ItemInsertMode ItemInsertMode)
      : this(anchor, separator, DynamicMenuType, ItemInsertMode, 4)
   {
   }

   // Dynamic menu item clicked event handler.
   private void MenuItemClick(object sender, EventArgs e)
   {
      DynamicMenuItem item = (DynamicMenuItem)sender;
      MessageBox.Show(item.Data);
   }

   // Add a group of dynamic menu items.
   public void AddMenuItems(DynamicMenuItem.DynamicMenuItemTextData[]
                            textDataCollection)
   {
      foreach (DynamicMenuItem.DynamicMenuItemTextData textData
               in textDataCollection)
         AddMenuItem(textData.MenuText, textData.MenuData);
   }

   // Add one dynamic menu item with menu text being paramter menuText
   // and menu data being parameter data.
   public virtual void AddMenuItem(string menuText, string data)
   {
      Menu.MenuItemCollection menuItems;
      DynamicMenuItem menuItem = new DynamicMenuItem(menuText,
         data, this.MenuItemClick);

      switch (_dynamicMenuType)
      {
         case DynamicMenuType.Inline:
            menuItems = _anchor.Parent.MenuItems;
            AddMenuItemInline(menuItem, menuItems);
            break;
         case DynamicMenuType.Submenu:
            menuItems = _anchor.MenuItems;
            AddMenuItemInSubmenu(menuItem, menuItems);
            break;
         default:
            break;
      }

   }

   // Add inline dynamic menu item. Parameter menuItems contains
   // existing inline dynamic menu items.
   private void AddMenuItemInline(MenuItem menuItem,
      Menu.MenuItemCollection menuItems)
   {
      int anchorIndex = _anchor.Index;
      switch (_itemInsertMode)
      {
         case ItemInsertMode.Append:
            if (_itemCount == _maxItems)
            {
               menuItems.RemoveAt(anchorIndex + 1);
               _itemCount--;
            }
            menuItems.Add(anchorIndex + _itemCount + 1, menuItem);
            break;

         case ItemInsertMode.Prepend:
            if (_itemCount == _maxItems)
            {
               menuItems.RemoveAt(anchorIndex + _maxItems);
               _itemCount--;
            }
            menuItems.Add(anchorIndex + 1, menuItem);
            break;
         default:
            break;
      }
      _itemCount++;
      _separator.Visible = true;
   }

   // Add dynamic menu item in a submenu. Parameter menuItems
   // contains existing menu items in the submenu.
   private void AddMenuItemInSubmenu(MenuItem menuItem,
      Menu.MenuItemCollection menuItems)
   {
      switch (_itemInsertMode)
      {
         case ItemInsertMode.Append:
            if (_itemCount == _maxItems)
            {
               menuItems.RemoveAt(0);
               _itemCount--;
            }
            menuItems.Add(menuItem);
            break;

         case ItemInsertMode.Prepend:
            if (_itemCount == _maxItems)
            {
               menuItems.RemoveAt(_maxItems-1);
               _itemCount--;
            }
            menuItems.Add(0, menuItem);
            break;
         default:
            break;
      }
      _itemCount++;
      _anchor.Enabled = true;
   }
}

About the Demo Project

The demo project was written using C# 2005. It illustrates the creation of two types of dynamic menus, each of which uses different menu item insertion mode—either prepend or append.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Protecting business operations means shifting the priorities around availability from disaster recovery to business continuity. Enterprises are shifting their focus from recovery from a disaster to preventing the disaster in the first place. With this change in mindset, disaster recovery is no longer the first line of defense; the organizations with a smarter business continuity practice are less impacted when disasters strike. This SmartSelect will provide insight to help guide your enterprise toward better …

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds