Click here to Skip to main content
Click here to Skip to main content

Implementing Dynamic Menus with C# and Reflection

, 22 Feb 2013
Rate this:
Please Sign up or sign in to vote.
Basic article to describe how to use Reflection for dynamically loading commands/etc

Introduction

There are plenty of questions answered on StackOverflow, and perhaps even some on CodeProject that may touch on this subject, but I thought it would be worthwhile to post.

I wanted a way to load in controls without having to type out a new one each time. The main hope (eventually) for this method is that I can extract out the commands into a separate DLL. That way when I go to update the program, I only have to update the command DLL and not send a whole new version. This helps to limit the size of my update file, and can also aid in customization of the program for individual customers. 

Background

This article assumes that you have basic knowledge of WPF or winforms controls. The code is quite simple (< 100 lines) but you will need at least a familiarity with WPF to read it.

Using the code

The example code provided includes several classes, all of which are kept small for this example. Let me run through them quick:

  • MainWindow:
  • The mainwindow is standard in any WPF GUI project, and is used here to be the window we dynamically load our commands into. It also houses the reflection logic that actually loads the controls in.

  • ICommand:
  • This is an interface class for all of the commands. This must be used so that reflection can find your command classes. If there is no interface class, there is nothing linking your separate command classes.

  • Commands:
  • This class is used for an enum only. This is not a necessary class, but you may find it useful for a more complicated implementation. Right now I keep a copy of the actual command on the MyButton class (see "MyButton" description). The reason I wouldn't do this in a more complicated program is that it is better to use a "SetCommand" method that will set the command based on what the enum is, rather than using an actual Command object. This prevents any multiple instances/leftover classes lingering around. Also, if you have other implementations of that class (another constructor, for instance),  you would want to create that one instead of the default. 

  • OpenCommand/SaveCommand:

    These are the basic commands being used. Notice that I only have "Name" and "Command" on them, along with the execute method. In more complicated implementations of this you may want to include tooltip, toolbar, toolbar location, etc. You may find that you want to put all this information in another class if you store too much as well.

    Important:  You must create a standard constructor for the below implementation. The CreateInstance the way I'm using it does not recognize parameters. You can achieve this, however, simply by doing CreateInstance(type, object[] parameters). It will find the constructor (if it exists) that best fits the parameter type. 

  • MyButton:

    Because we need to store either the enum or a reference to the command, we must extend the Button into our own. In a more complicated implementation of this, I've actually created a user control that houses a button, and extended it that way for a little more flexibility. 

Let's look at the main code used for reflection: 

private void DynamicallyLoadCommands()
{
    Type type = typeof(ICommand);
    
    //get list of classes that implement the ICommand interface
    var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
        .SelectMany(s => s.GetTypes())
        .Where(p => type.IsAssignableFrom(p));
    
    ToolBar bar = new ToolBar();
    
    foreach (Type classType in types)
    {
        //since ICommand is included in our list, we must ignore it.
        if (classType.Name == "ICommand") { continue; }

        ICommand command = (ICommand)Activator.CreateInstance(classType);
        MyButton button = new MyButton();
        button.Content = command.Name;
        button.command = command;
        button.Click += new RoutedEventHandler(button_Click);
        bar.Items.Add(button);
    }
    CommandPanel.Children.Add(bar);
}

The first thing we do is acknowledge that we're going to be getting files of type ICommand (our implementation, not the System.Windows.Input one. Be careful!) 

Next we use some LINQ to select our assembly types where type.IsAssignableFrom(p). This finds any classes that are linked to our ICommanddd interface.

Skipping over the toolbar creation, we enter a loop that cycles through any classes of that type, ignoring ICommand itself, and creates an instance of it. From that instance we are able to get the Name, and the command itself and assign it to MyButton.

The last thing we do is add our button to our Toolbar, and add the Toolbar to a "CommandPanel", which is just a StackPanel in WPF.

Points of Interest 

As mentioned, using Activator.CreateInstance(classType) will use the default constructor. If you have constructors in your commands that you need parameters for, you can use Activator.CreateInstance(classType, parameters). Keep in mind though that this is cycling through all of your commands, so you will have to handle any special cases with different parameters. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

oppassum

United States United States
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 22 Feb 2013
Article Copyright 2013 by oppassum
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid