Thursday, November 19, 2009

Dynamic Menu Items

There was a question recently on the Universal Thread about dynamically adding MenuItems to a Form. The data for these dynamically added Items can be obtained from anywhere, like from your database. This fits in quite nicely with the first substantial post I wrote in this blog back in September, Reflection in .NET, because you can use these dynamically added Items to launch new DLLs that you might create (for example, a custom form for one of your customers), via Reflection, without having to recompile and re-distribute your entire application. Just supply the newly-created DLL. At my last company, we used DevExpress SideBars and incorporated this dynamic functionality that way.

So, without further ado, here’s the first thing: the code you need to dynamically add the menu items. Note that this creates different sub-classes of MenuItems, depending on your data:

private void TestMenuAddItemsDynamically()
{
MenuItem item; // This is so you can use MenuItem sub-classes
foreach (DataRow row in this.oData.Rows)
{
if (string.IsNullOrEmpty(row["assembly"].ToString()) == false)
{
item = new LaunchMenuItem(row["description"].ToString(), row["assembly"].ToString(), row["class"].ToString());
this.AddMenuItem("Custom Forms", item);
}
else
if (string.IsNullOrEmpty(row["url"].ToString()) == false)
{
item = new FavoritesMenuItem(row["description"].ToString(), row["url"].ToString());
this.AddMenuItem("Favorites", item);
}
else
{
item = new DefaultMenuItem(row["description"].ToString());
this.AddMenuItem("Dynamic Menu", item);
}
}
}

Next, I’ll show you the LaunchMenuItem class, since it is what this post is focusing on.

// Used to launch a DLL by Reflection
public class LaunchMenuItem : MenuItem
{
protected string AssemblyName;
protected string ClassName;

public LaunchMenuItem(string text, string assemblyName, string className)
: base(text)
{
this.Click += new System.EventHandler(this.ClickHandler);
this.AssemblyName = assemblyName;
this.ClassName = className;
}
protected virtual void ClickHandler(object sender, System.EventArgs e)
{
MyReflectionClass oReflect = new MyReflectionClass(this.AssemblyName, this.ClassName);

// In it's simplest usage, I will assume that the class I am instantiating is a Form
// Expanding on this concept is left as an exercise for the reader.
// I've also left out error checking for the instantiated object

string message = "";
Form oForm = oReflect.InstantiateClass(ref message) as Form;
if (oForm == null)
MessageBox.Show(message);
else
oForm.Show();
}
}

Note that the above class uses the MyReflectionClass from my previous post on the subject, Reflection In .NET.

The other custom MenuItem classes would contain other things to do in their Click event handlers, such as browsing to a URL link inside a BrowserControl on your Form (for the FavoritesMenuItem class). I’m not including that code in this example … I leave it as an exercise for the reader.

There’s one more method to show, and that’s a common method that will add any MenuItem to the Form’s Menu. Notice that it does things like check to see if the top menu item already exists (“Custom Forms”, “Favorites” and “Dynamic Menu” in the above example). It adds it if it doesn’t, and then adds the new MenuItems under it.

// Generic method for adding to any Menu
private void AddMenuItem(string name, MenuItem item)
{
string lookForName = name.Replace("&", "");

// Find top-level menu item
MenuItem addTo = null;
for (int i = 0; i < this.Menu.MenuItems.Count; i++)
{
MenuItem topLevel = this.Menu.MenuItems[i];
if (string.Compare(topLevel.Text.Replace("&", ""), lookForName, true) == 0)
{
addTo = topLevel;
break;
}
}

// Was a top-level menu item found?
if (addTo == null)
{
addTo = new MenuItem(name);
this.Menu.MenuItems.Add(addTo);
addTo.Index = this.Menu.MenuItems.Count - 2;
}

addTo.MenuItems.Add(item);
}

That about wraps it up. I hope this has given my readers lots of ideas to go forth and try out some cool dynamic stuff.

No comments:

Post a Comment