Command,Pattern

Erhan 8/7/2016 0

Command Pattern

C#
 Challenge
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.
What would be your approach?
Definition
GoF Definition: "Encapsulate a request as an object, thereby letting you
parameterize clients with different requests, queue or log requests, and support
undoable operations"
Implementation
We can use the Command Pattern in the above scenario.
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:
The SquareCommand class for square drawing operation and RectangleCommand
class for rectangle operations.
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.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommandPattern.Classes
{
    public interface ICommand
    {
        void Do();
        void Undo();
    }
}


**********************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using CommandPattern.Classes;
using System.Collections;

namespace CommandPattern.CommandPattern
{
    public class RectangleCommand : ICommand
    {
        private Point _point;
        private Bitmap _bitmap;
        private Graphics _graphics;
        private Color _color;

        public RectangleCommand(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 = 100;
        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  ]);

        }
    }
}

*****************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using CommandPattern.Classes;

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  ]);

    }
}
***********************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CommandPattern.Classes;
using CommandPattern.CommandPattern;

namespace CommandPattern
{
    public partial class MainForm : Form
    {
        private ShapeEnum _activeShape = ShapeEnum.Square;
        private Color _activeColor = Color.Blue;

        private Bitmap _bitmap;
        private Graphics _graphics;

        public MainForm()
        {
            InitializeComponent();

            _bitmap = new Bitmap (Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height);
            Canvas.Image = _bitmap;
            _graphics = Graphics.FromImage(Canvas.Image);
            CreateColorPickerPanels();
        }

        #region "UI Code"

        /// <summary>
        /// Randomly create colors
        /// </summary>
        private void CreateColorPickerPanels()
        {
            Random random = new Random(100);

            for(int i = 1; i <= ColorTable.RowCount; i  )
                for (int j = 1; j <= ColorTable.ColumnCount; j  )
                {
                    Panel panel = new Panel();
                    panel.BackColor = Color.FromArgb(random.Next(255), random.Next(255), random.Next(255));
                    panel.Cursor = Cursors.Hand;
                    panel.Click  = new EventHandler (colorPanel_Click);

                    ColorTable.Controls.Add(panel);
                }
        }

        private void colorPanel_Click(object sender, EventArgs e)
        {
            _activeColor = (sender as Panel ).BackColor;
        }

        private void SquareButton_Click(object sender, EventArgs e)
        {
            _activeShape = ShapeEnum.Square;
        }

        private void RectangleButton_Click(object sender, EventArgs e)
        {
            _activeShape = ShapeEnum.Rectangle;
        }
       
        #endregion

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

        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();
        }

        private void RefreshUI()
        {
            Canvas.Refresh();

            // Disable Undo button if no more commands available
            UndoButton.Enabled = _commandStack.Count > 0;
        }

        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();
        }

        private void ClearMenu_Click(object sender, EventArgs e)
        {
            // Clear - continuously perform undo
            while (_commandStack.Count > 0)
                _commandStack.Pop().Undo();

            RefreshUI();
        }
    }

    public enum ShapeEnum
    {
        None,
        Square,
        Rectangle
    }
}



  
 

Report Bug

Please Login to Report Bug

Reported Bugs

Comments

Please Login to Comment

Comments