Command Pattern - Undo Example


Friends, here I am trying to show the usage of Command Design Pattern, one among 23 design patterns.

Personally I like this pattern very much, it is making use of good object oriented constructs and simplifies the logic of enabling Undo. 

Scenario

You are working on a painting application.  For each brush stroke you need to provide the Undo feature.

The brushes presently available are square and rectangle. In future more and more brushes will be available.  Our challenge is to provide the user with Undo feature. Clearly, on clicking the undo button, the last stroke should be cleared from the canvas.

CommandPattern.png
 
Command Pattern applied to the scenario

The definition of Command Pattern says "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations"

Command Pattern Explained

The pattern says that all the operations should be converted into objects, so that we log the objects.  In future we can call Do() or Undo() operations on the object.   The object should be smart enough to store the request parameters inside it to support the Do and Undo operations.

Command Pattern for our Scenario

Applied to our scenario, we have got two brushes:
  • Square Brush
  • Rectangle Brush
The other parameters would be Canvas which is the PictureBox control on which we draw and the X, Y location on mouse pointer on the canvas.   Formulating this we can create 2 classes:

SquareCommand class for square drawing operation and Rectangle class for rectangle drawing operation.

The Do() operation would be drawing a square for the square class and drawing a rectangle for the rectangle class.

We can formulate an interface called ICommand having the Do() and Undo() methods.

public interface ICommand
{
    void Do();
    void Undo();
}

The classes would be SquareCommand and RectangleCommand which implement s the above interface.

Architecture - Class Diagram

2.gif
 
Implementation

When the application is executed, the user will be having a blank white screen, with a set of colors to choose from.  The default brush selected will be Square and on clicking the canvas a new square will be drawn on the canvas.  The default color selected is Blue and is stored in the variable _activeColor.

The form level variables declared are:

private ShapeEnum _activeShape = ShapeEnum.Square;
private Color _activeColor = Color.Blue;
private Bitmap _bitmap;
private Graphics _graphics;

For each mouse down operation a new square class will be created.  That means if 10 clicks are made 10 square classes will be created and the Do() operation is called   An enumeration variable called _activeShape is used to keep track of the current shape selected by the user.  The mouse down event would look like the following.

private void Canvas_MouseDown(object sender, MouseEventArgs e)
{
    // Each mouse down creates a new command class instance
     ICommand command = null;
     if (_activeShape == ShapeEnum.Square)
         command = new SquareCommand(_bitmap, _activeColor, e.X, e.Y);
    else if (_activeShape == ShapeEnum.Rectangle)
         command = new RectangleCommand(_bitmap, _activeColor, e.X, e.Y);
    command.Do();
    _commandStack.Push(command);
    RefreshUI();
}

Let us take an example: assume the user clicked the mouse at Point X=100 and Y=200.  As the _activeShape is Square,  a new SquareCommand class instance is created with arguments e.X and e.Y.  The remaining arguments are bitmap and the active color.  Then the command instance would be pushed to a stack and the Do() operation is called.  

The stack provides a storage for usage of the command instance later.  The declaration of stack is:

private Stack<ICommand> _commandStack = new Stack<ICommand>();

Inside the SquareCommand Class

We can have a look on the Square class.  Both the SquareCommand and RectangleCommand class will be having same implementations except in the Do() operation of drawing the respective shape.

public class SquareCommand : ICommand
{
    private Point _point;
    private Bitmap _bitmap;
    private Graphics _graphics;
    private Color _color;
    public SquareCommand(Bitmap bitmap, Color color, int x, int y)
    {
        _bitmap = bitmap;
        _graphics = Graphics.FromImage(_bitmap);
        _color = color;
        _point = new Point(x, y);
    }
    public void Do()
    {
        // Save the current pixel colors for a future UNDO perform
        SaveCurrentPixels();
        // Do the drawing
        _graphics.FillRectangle(new SolidBrush(_color),
          new Rectangle(_point.X, _point.Y, Width, Height));
    }
    private const int Width = 50;
    private const int Height = 50;
    private IList<Color> _colors = new List<Color>();
    private void SaveCurrentPixels()
    {
        for (int i = _point.X; i < _point.X + Width; i++)
            for (int j = _point.Y; j < _point.Y + Height; j++)
                _colors.Add(_bitmap.GetPixel(i, j));
    }
    /// <summary>
    /// Perform Undo by restoring back the pixels to previous colors
    /// </summary>
    public void Undo()
    {
        int ix = 0;
        for (int i = _point.X; i < _point.X + Width; i++)
            for (int j = _point.Y; j < _point.Y + Height; j++)
                _bitmap.SetPixel(i, j, _colors[ix++]);
    }
}

The constructor is called with the bitmap and the x, y positions which are then stored into class fields.  When the Do() method is called the current pixel colors are saved into the _colors list.  This would enable us to perform the Undo() method later.  After that the graphics.FillRectangle() method is called to draw the square.  The width and height of the square would be set to 50 pixels using the constants Width and Height respectively.

The Undo() method just restores the previous pixel values using the bitmap.SetPixel method() by iterating through each pixel starting from x, y position.

Performing the Undo() on clicking Undo button

As we encapsulated each operation to a class with parameters and logged them into the command stack, it is now easier to call the Undo() method.  Please remind that on clicking the Undo button we have to get the last operation instance and call the Undo() method of it, and removing it from the stack.

The code for it is:

private void UndoButton_Click(object sender, EventArgs e)
{
    // Check command stack contains items
    if (_commandStack.Count > 0)
    {
        // Remove the last command
        ICommand lastCommand = _commandStack.Pop();
        // Call the Undo method
        lastCommand.Undo();
}
      RefreshUI();
}

First, the command stack count is checked to ensure there are commands inside it.  Then the last command is popped out using the Pop() method.  It will give us the last instance in the stack as well as removes it.  Then, the Undo() operation is invoked. After, the RefreshUI() method is called to update the UI with the changes.

Note

The command pattern provides an object oriented manner to provide Undo feature to our application in a flexible manner.

But the creation of lot of object instances is one of the draw back of this method which usually people say about.  I would say that in the other hand, the command pattern is giving us the advantage of keeping the code more manageable.

Up Next
    Ebook Download
    View all
    Learn
    View all