One of the vast improvements offered by C# over Visual Basic 6.0 is the addition of a truly dynamic menu system. With this new functionality, developers can create applications that adapt themselves quickly to new operating environments and software patches, while improving the overall ease-of-use for your software. The trick is to leverage the C# menu capabilities appropriately.
Dynamic menus, unlike other menus, are added at runtime and represent new functionality that wasn't present before. Dynamic menus shouldn't be confused with options that are hidden because a feature is disabled or inapplicable to the current context. Nor do they represent menus that are strictly disabled. Dynamic menus bring something new and external to the application.
While the application of dynamic menus provides numerous advantages to an application, I will focus on their implementation through the addition of new program features via plug-ins, which is the most common use of dynamic menus. This system, however, could easily be extended to include applications that support dynamic updating, with newer versions being downloaded and applied to the user's computer as soon as the new features are available.
.NET menu system
Unlike VB 6, which drew no distinction between main menus and context menus, C# clearly delineates between the two. Main menus, as the name implies, represent those visible on the top of a form. Typically these menus include such elements as File, Edit, Help, and View. Context menus are usually prompted to appear by a right mouse-click but can show up under other circumstances as well. By definition, context menus are designed to provide functionality relevant to the current status, or context, of the application. In Microsoft Word, for example, a context menu offers increased functionality when editing a misspelled word.
Although the two menu systems have technical independency, they are both derived from the same root object, System.Windows.Forms.Menu. As such, the operations necessary to generate both context and main menus share several common features.
Adding menu items
The first critical component to providing a dynamic menu element in either the main form window or in a context menu is to insert a new menu item into the menu. This is done through a function of the parent Menu class and therefore operates the same way for both main menus as well as context menus. To insert a new menu item, the application needs only to create a new MenuItem object and call the Add function of the MenuItemCollection contained in the class (either main menu or context menu) to which the new menu item is to be added. The application code shown below will help to illustrate this point. This simple application provides only a single text box and a button. The click event of the button is used to create a new menu item with the name supplied inside the text box.
private void AddMenu_Click(object sender,
MenuItem newItem = new MenuItem();
newItem.Text = this.textBox1.Text;
newItem.Index = this.mainMenu1.MenuItems.Count;
Again, the code is very straightforward: A new MenuItem is created, the text for the menu is set equal to the text supplied in the textBox, the menu item's order in the menu chain is set as the last item in the menu, and the new menu is inserted into the larger menu. The result is a dynamic modification to the main menu of the application. Again, since both context menus and main menus are derived from the base Menu class, the process for inserting a menu item into a context menu is identical.
Developing a dynamic system
In a real-world environment, the application shown above would need to supply the new menu code dynamically. There are several options for this. The easiest and most likely would be to use object abstraction to create the MenuItem object. The plug-in assembly could then be dynamically loaded with a derived object from the constructed assembly. This object would then be responsible for producing its own MenuItem object, which would then be loaded into the menu.
This method simplifies the association of the code to the object and avoids the need for numerous callback functions. While a complete overview of the steps involved in this process is out of the scope of this article (it relates more to the common language runtime reflection and dynamic assembly execution than dynamic menus), a brief description of the steps involved can be provided.
- Locate the Assembly DLL—This step can be as simple as looking in a directory for the addition of a new file or just asking the user to load the plug-in by specifying its path.
- Negotiate a plug-in API—The easiest way to do this is to publish an interface that must be present in the remote DLL. The presence of any class derived from this interface would indicate an eligible plug-in.
- Load the DLL—Assemblies can be dynamically loaded through the .NET assembly class using the LoadFrom method.
- Validate plug-in status—Simply ensure that one class inside the assembly supports the interface you’ve defined. This can also be done through reflections.
- Instantiate a class instance—Once the assembly has been loaded, the Assembly class’s CreateInstance method can be used to create a new instance of the object in the class that supports your interface.
- Get the MenuItem—Simply specify that one of the functions in your required interface must support a GetMenuItem method. That method will allow the class to return a MenuItem object with all of the functionality provided.
- Add the MenuItem—Just use the code demonstrated above and you will be finished.