I'm trying to add unlimited undo/redo functionality to my application. I'm making use of the Command Pattern
. When I add an Ellipse
in Form1
, it's added to commandList
. But when the user resizes/moves a shape, I also want to add that command to commandList
, how can I do this because adding happens in Form1
and resizing/moving in Ellipse
. How to fill in the Execute
and UnExecute
method of Ellipse
? The 'walking' trough the list with the Redo
/Undo
button already works, you can see it in the picture. In panel_MouseUp
you see a Box
, this is also a custom Control
almost identical to Ellipse
, you can comment the case
out to get a runnable example.
ICommand.cs
public interface ICommand
{
void Execute();
void UnExecute();
}
Ellipse.cs
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);
}
public void Execute()
{
Console.WriteLine("Execute command");
}
public void UnExecute()
{
Console.WriteLine("Unexecute command");
}
}
Form1.cs
public partial class Form1 : Form
{
private bool draw;
private int x, y, xe, ye;
private List<ICommand> commandList = new List<ICommand>();
int current = 0;
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);
//command execute
el.Execute();
commandList.Add(el);
current++;
break;
default:
break;
}
}
private void undoButton_Click(object sender, EventArgs e)
{
if (current > 0)
{
ICommand command = commandList[--current];
//Command unexecute
command.UnExecute();
}
panel.Invalidate();
}
private void redoButton_Click(object sender, EventArgs e)
{
if (current < commandList.Count)
{
ICommand command = commandList[current++];
//Command execute
command.Execute();
}
panel.Invalidate();
}
private void clearAllButton_Click(object sender, EventArgs e)
{
commandList.Clear();
current = 0;
panel.Controls.Clear();
}
}
Aucun commentaire:
Enregistrer un commentaire