Sunday, December 3

Using attributes to specify the contents of a menu

Most people think it is a good idea to separate business and presentation logic in your application, so you can update one without changing the other. When you create an application with a menu bar, the menu items will often call functions in your business logic, and if you add a new business logic function you will need to add a handler in your presentation layer and the business logic function in your business logic layer.


This article describes how you can add attributes in your business logic layer and have these dynamically bound to a menu when your application runs. The attributes will look like the following:


   public class MenuHandler
{
[MenuOption ("Menu Option 1")]
public void MenuHandler(object sender,
EventArgs e)
{
//Business Logic
}
}


Creating your Attribute

The first thing you need to do is to set up your attribute. Create a class derived from System.Attribute as follows.


    [AttributeUsage(AttributeTargets.Method)]
public class MenuOptionAttribute :
System.Attribute
{
private string MenuText;

public MenuOptionAttribute(String
menutext)
{
MenuText = menutext;
}

public string GetMenuText
{
get
{
return MenuText;
}
}
}


The first line of this specifies your attribute can be applied to methods (and not to classes). The constructor takes one argument, which is a string representing the menu text.


Applying your attribute to your business logic

Next, you write your business logic layer to use the MenuOption attribute. All you need to do is to put this before any functions you want on your menu, with the text to go on your menu:


    public class BusinessLogic
{
[MenuOption ("Menu Option 1")]
public void Option1(object sender,
EventArgs e)
{
// Option 1 code
}

[MenuOption("Option 2")]
public void Option2(object sender,
EventArgs e)
{
// Option 2 code
}

You can add as many of these functions as you like.


Setting up your menu

Finally, you need to create your presentation layer. You can drag a MenuStrip to a form and add a title for your drop down menu. If you call the title "Main", when you add the title, it will create a ToolStripMenuItem, called "mainToolStripMenuItem" by default.


You need to use reflection to iterate over all the methods in the MenuHandler class, and see which of those methods have the MenuOptionAttribute attribute set.


If it is set, you need to call the GetMenuText() function on the attribute to find out the text to display on the menu bar and add the menu item.


Next, you need to create a delegate to your method.


Finally, you add the event handler to your newly created menu item. It's easier to understand this if you read the code example.


  public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BusinessLogic bl = new BusinessLogic();

mainToolStripMenuItem.DropDownItems.
Clear();
foreach (MethodInfo method in
(typeof (BusinessLogic)).GetMethods())
{
foreach (object attribute in
method.GetCustomAttributes(true))
{
if (attribute is
MenuOptionAttribute)
{
ToolStripItem newitem =
mainToolStripMenuItem.DropDownItems.Add((
attribute as MenuOptionAttribute).GetMenuText);
EventInfo ci = typeof
(ToolStripItem).GetEvent("Click");
Type tdelegate = ci.
EventHandlerType;
Delegate del = Delegate.
CreateDelegate(tdelegate,bl,method);
ci.AddEventHandler(
newitem, del);
}
}
}
}
}

You could add some code to your attribute to specify its position, and use this to order your menu items. You might want to specify more than one string, one for the heading and one for the item itself, so you can put menu items in under different headings.