Hi Jeff,
I wrote a pretty short Forms-application with a mini-menu (File -> New,
Print, Exit). Can you help me (and others) to turn this into a program
that implements the Command Pattern? I'd love to see it
The following is an incredibly simple recreation of the program you posted,
along with keyboard shortcut support. By itself, it just makes the code
overly complicated. The concept, however, becomes far more powerful as the
application becomes more complex.
Note that I created two dictionaries: one to hold the commands (with a
made-up ID) and one to hold a cross-reference (an index) for keyboard
shortcuts. It's doubtful that every single one of your menu items will have
a keyboard shortcut, so you'll end up with more items in the command list
than you will in the index. (In this example, 3 commands, 2 shortcuts).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace CommandPattern
{
//class Form
class MainForm : System.Windows.Forms.Form
{
//data menustrip
System.Windows.Forms.MenuStrip menustrip = new
System.Windows.Forms.MenuStrip();
//data menuitem1, menuitem2, menuitem3, menuitem4
System.Windows.Forms.ToolStripMenuItem menuitem1 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem2 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem3 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem4 = new
System.Windows.Forms.ToolStripMenuItem();
private Dictionary<int, ICommand> _commands;
private Dictionary<Keys, int> _keysToCommandsXref;
private const int NEW_COMMAND_ID = 1;
private const int PRINT_COMMAND_ID = 2;
private const int EXIT_COMMAND_ID = 3;
//constructor
public MainForm()
{
// Set up the commands and connect them to keyboard shortcuts, if
any
_commands = new Dictionary<int, ICommand>();
_keysToCommandsXref = new Dictionary<Keys, int>();
// The New command plus its keyboard shortcut: Ctrl+N
_commands.Add(NEW_COMMAND_ID, new NewCommand());
_keysToCommandsXref.Add(Keys.Control | Keys.N, NEW_COMMAND_ID);
// The Print command plus its keyboard shortcut: Ctrl+P
_commands.Add(PRINT_COMMAND_ID, new PrintCommand());
_keysToCommandsXref.Add(Keys.Control | Keys.P, PRINT_COMMAND_ID);
// The Exit command. It needs no keyboard shortcut because of the
// system-wide Alt+F4.
_commands.Add(EXIT_COMMAND_ID, new ExitApplicationCommand());
Text = "The Command Pattern";
menuitem1.Text = "File";
menuitem2.Text = "New";
menuitem2.Tag = NEW_COMMAND_ID;
menuitem3.Text = "Print";
menuitem3.Tag = PRINT_COMMAND_ID;
menuitem4.Text = "Exit";
menuitem4.Tag = EXIT_COMMAND_ID;
menuitem2.Click += OnMenuItemClick;
menuitem3.Click += OnMenuItemClick;
menuitem4.Click += OnMenuItemClick;
menuitem1.DropDown.Items.AddRange(
new System.Windows.Forms.ToolStripMenuItem[]
{
menuitem2,
menuitem3,
menuitem4,
});
menustrip.Items.Add(menuitem1);
Controls.Add(menustrip);
Show();
}
//method OnMenuItemClick
private void OnMenuItemClick(object sender, System.EventArgs e)
{
int commandId = (int)((ToolStripMenuItem)sender).Tag;
if (_commands.ContainsKey(commandId))
{
_commands[commandId].Execute();
}
else
{
// Oops!
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (_keysToCommandsXref.ContainsKey(e.KeyData))
{
_commands[_keysToCommandsXref[e.KeyData]].Execute();
}
else
{
base.OnKeyUp(e);
}
}
//method Main
static void Main()
{
System.Windows.Forms.Application.Run(new MainForm());
}
}
// There is actually an existing interface that is exactly like this:
// System.Windows.Forms.ICommandExecutor
public interface ICommand
{
void Execute();
}
internal class NewCommand : ICommand
{
public void Execute()
{
MessageBox.Show("New");
// In a real implementation, you'd have to provide a
// means for the command to talk to the main form (or
// perhaps some sort of shared class) so that it can
// instruct the target to create a new document
}
}
internal class PrintCommand : ICommand
{
public void Execute()
{
MessageBox.Show("Print");
}
}
internal class ExitApplicationCommand : ICommand
{
public void Execute()
{
Application.Exit();
}
}
}