dimanche 24 mai 2015

Command pattern push all commands to stack

I have two custom Control classes, Boxand Ellipse. I'm able to draw, drag and resize them. Now I'm trying to implement unlimited undo and redo. I use the Command Pattern for this. My Box class look exactly the same as the Ellipse class with the difference that the Box class paints a box shape. At the moment when I paint an Ellipse and for example move /resize this control I'm able to go one step back, because adding makes the stacksize 1 and one undo makes the stacksize 0 again. Only when I add the Ellipse I push the command to the Stack now. The resizing/moving happens in another class then the adding, but these actions also have to be pushed to the commandStack in Form1. How can I do this?

My idea was to create a stack in Ellipse and Boxand call Do for every action and push every command to the stack (in Ellipse or Box), but this isn't gonna work I believe because the Form class needs to have access to the stack with all commands from all Controls. Anyone who can help to make unlimited undo/redo work? I followed the example in this link: http://ift.tt/1RdUEei .

ICommand interface

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

Ellipse class

class Ellipse : Control, ICommand
{
    private Point mDown { get; set; }

    public Ellipse()
    {
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        this.BackColor = Color.Transparent;
        this.DoubleBuffered = true;
        this.ResizeRedraw = true;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        // Draw a black ellipse in the rectangle represented by the control.
        e.Graphics.FillEllipse(Brushes.Black, 0, 0, Width, Height);

    }  

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        mDown = e.Location;
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        // Call MyBase.OnMouseMove to activate the delegate. 
        base.OnMouseMove(e);


        if (e.Button == MouseButtons.Left)
        {
            Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
        }

    }

    /* Allow resizing at the bottom right corner */
    protected override void WndProc(ref Message m)
    {
        const int wmNcHitTest = 0x84;
        const int htBottomLeft = 16;
        const int htBottomRight = 17;
        if (m.Msg == wmNcHitTest)
        {
            int x = (int)(m.LParam.ToInt64() & 0xFFFF);
            int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
            Point pt = PointToClient(new Point(x, y));
            Size clientSize = ClientSize;
            if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
            {
                m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
                return;
            }
        }
        base.WndProc(ref m);
    }

    int x;
    int y;
    int height;
    int width;


    private void SaveCurrentControl()
    {
        x = this.Left;
        y = this.Top;
        height = this.Height;
        width = this.Width;
    }

    public void Do()
    {
        SaveCurrentControl();
    }

    public void Undo()
    {
        this.Left = x;
        this.Top = y;
        this.Height = height;
        this.Width = width;
    }
}

Form class

public partial class Form1 : Form
{
    private bool draw;
    private int x, y, xe, ye;
    private Stack<ICommand> commandStack = new Stack<ICommand>();
    ICommand command  = null;

    public Form1()
    {
        InitializeComponent();

        menuComboBoxShape.ComboBox.DataSource = Enum.GetValues(typeof(Item));
    }

    public enum Item
    {
        Pencil,
        Rectangle, 
        Ellipse,
    }


    private void panel_MouseDown(object sender, MouseEventArgs e)
    {
        draw = true;
        x = e.X;
        y = e.Y;
    }

    private void panel_MouseUp(object sender, MouseEventArgs e)
    {
        draw = false;
        xe = e.X;
        ye = e.Y;

        Item item; 
        Enum.TryParse<Item>(menuComboBoxShape.ComboBox.SelectedValue.ToString(), out item);

        switch (item)
        {

            case Item.Pencil:
                using (Graphics g = panel.CreateGraphics())
                    using (var pen = new Pen(System.Drawing.Color.Black))     //Create the pen used to draw the line (using statement makes sure the pen is disposed)
                    {
                        g.DrawLine(pen,new Point(x, y), new Point(xe, ye));
                    }
                break;
            case Item.Rectangle:
                var box=new Box();
                panel.Controls.Add(box);
                box.Location = new Point(x, y);
                box.Width = (xe - x);
                box.Height = (ye - y);
                break;
            case Item.Ellipse:
                var el = new Ellipse();
                panel.Controls.Add(el);
                el.Location = new Point(x, y);
                el.Width = (xe - x);
                el.Height = (ye - y);
                el.Do();
                commandStack.Push(el);
                break;
            default:
                break;
        }
    }

    private void undoButton_Click(object sender, EventArgs e)
    {
        if (commandStack.Count > 0)
        {
            ICommand lastCommand = commandStack.Pop();
            lastCommand.Undo();
        }
        panel.Invalidate();
    }

    private void clearAllButton_Click(object sender, EventArgs e)
    {
        panel.Controls.Clear();
    }
}

Aucun commentaire:

Enregistrer un commentaire