Simple Command Mapping Infrastructure in .Net

Unlike MFC, .Net does not offer a built-in framework for message handling in WinForm applications. MFC developers, for better or worse, have a rather large mechanism that automatically maps command messages from menus, toolbars, and windows into the classes that want them.

.Net has no such all-encompassing framework, though some 3rd-party frameworks exist (perhaps I’ll go over some in a a future entry).

The lack of a built-in application framework gives developers more freedom by not locking them into a certain mind set, but it also means that you have to develop your own framework or do everything from scratch in every application.

Short of developing an entire framework, there are some practices you can follow in .Net (and any GUI application, for that matter) to ease management of commands.

I’ll talk about one possibility that I recently chose to implement in a small WinForms application. First of all, let’s look at the overall architecture I’m aiming for:

It’s roughly a Model-View-Controller (MVC) architecture. What I want to do is almost completely isolate the GUI layer from knowledge of how the program actually works. The breakdown of responsibility is as follows:

  1. GUI – contains menus, status bar, views, panels, toolbars, etc. and routes messages from them to the controller via command objects.
  2. Controller – coordinates views, reports, and GUI.
  3. ReportGenerator – the “model” in this architecture. Generates and stores the actual data that will be presented.
  4. Views – Does the drawing for the report in different ways.

With this architecture, the GUI is restricted to doing only a few things, all of them related to the state of the GUI:

  1. Receiving events from toolbar buttons, etc.
  2. Setting menu/button states based on general program state (i.e., report running, idle)
  3. Changing the current view based on a request from controller.
Command Objects

A popular way to handle commands from toolbars and menus is to use the Command design pattern. The advantages to this pattern are numerous, but as far as isolating the GUI from implementation details, they allow us to put all the work required to get things done into command objects and the controller.

Here’s a sample design for commands. As you can see, it’s exceedingly simple.

An interface defines the basic functionality: Execute(). The concrete base classes are created with parameters that tell it the information it needs to know. When the user selects a command in the GUI, the GUI must then run the associated command.

For example, here’s a command that merely changes the current view:

class ChangeViewCommand : ICommand
{
private ViewType viewType;

public ChangeViewCommand(ViewType view)
{
this.viewType = view;
}

#region ICommand Members

public void Execute()
{
DiskViewController.Controller.SetCurrentView(viewType);
}

#endregion
}

When the command is created, it is initialized with the view type. When it’s executed, it merely calls the controller to do the work. The amount of work that is done in the controller versus command objects really depends on the application and individual commands. In this specific case, it might not seem like it’s doing a lot, but it would be easy to add Undo support, logging, coordination among multiple classes, or any other requirement for a command. In addition, the benefit of using command objects even in simple cases like this will become clear shortly.

Associating GUI objects with Command objects

Many applications use menus as well as toolbars with duplicate functionality. There are also keyboard shortcuts that can invoke the same command. An easy way in .Net to associate the GUI with command objects is to use a hash table. The keys consist of the menu and toolbar objects, and the values are the command objects.

//declaration:
private Dictionary<object, ICommand> commandMap = new Dictionary<object, ICommand>();

//toolbar
commandMap[toolStripButtonPie] = new ChangeViewCommand(ViewType.Pie);
commandMap[toolStripButton3DPie] = new ChangeViewCommand(ViewType.Pie3D);
commandMap[toolStripButtonStart] = new StartCommand(controller);
commandMap[toolStripButtonStop] = new StopCommand(controller);

//menu
commandMap[pieToolStripMenuItem] = commandMap[toolStripButtonPie];
commandMap[Pie3DToolStripMenuItem] = commandMap[toolStripButton3DPie];
commandMap[startAnalysisDriveToolStripMenuItem] = commandMap[toolStripButtonStart];
commandMap[stopAnalysisToolStripMenuItem] = commandMap[toolStripButtonStop];

 You can see that some commands take the controller as argument–an option to discovering it from a static class variable. Also notice that commands from menu objects can reuse the same object that is associated with the toolbar button.

In order to run the commands, the GUI needs only respond to OnClick events from menus and toolbars and in many cases, the same delegate can be assigned to many of the menu items:

private void OnCommand(object sender, EventArgs e)
{
    if (commandMap.ContainsKey(sender))
    {
        ICommand cmd = commandMap[sender];
        if (cmd != null)
        {
              cmd.Execute();
              //could also do controller.RunCommand(cmd);
        }
    }
    else
    {
        throw new NotImplementedException();
    }
}

In conclusion, using the techniques presented here you can easily split your program into manageable chunks, with each layer doing only what it needs to.