我正在为我的图形编辑器添加撤消/重做功能,但是我在移动和调整大小时遇到了一些undo / redo问题。
首先,当我移动Control
(例如Ellipse
)时,我点击撤消按钮Ellipse
转到另一个位置。当我在撤消后按重做时Ellipse
转到正确的位置。
这是一个链接,可以更清楚地表达我的意思:http://gyazo.com/d299fe5e52f08742e7fa2132f6ff9839
与第一件事有很多协议的第二件事是当我调整Control
(例如Ellipse
)的大小时,我点击撤消Ellipse
的大小比它大应该有。再次单击重做后,它再次具有正确的大小。
这是一个链接,可以更清楚地表达我的意思:http://gyazo.com/59ecdcba751ff3b8ffd053dd19cd9945
问题可能在MoveCommand
和ResizeCommand
类中,我的代码基于此代码http://www.codeproject.com/Articles/33384/Multilevel-Undo-and-Redo-Implementation-in-Cshar。在链接页面中,他们使用WPF
和Thickness
用于移动/调整大小。在WinForms
中没有Thickness
,但我还尝试了Margin
/ Padding
,但没有运气。如何调整大小/移动撤消/重做工作?
MoveCommand.cs
class MoveCommand : ICommand
{
Control _control;
int _changeOfTop;
int _changeOfLeft;
public MoveCommand(int top, int left, Control control)
{
_control = control;
_changeOfTop = top;
_changeOfLeft = left;
}
public void Execute()
{
_control.Top = _control.Top + _changeOfTop;
_control.Left = _control.Left + _changeOfLeft;
}
public void UnExecute()
{
_control.Top = _control.Top - _changeOfTop;
_control.Left = _control.Left - _changeOfLeft;
}
}
ResizeCommand.cs
class ResizeCommand : ICommand
{
private int _changeOfWidth;
private int _changeOfHeight;
private Control _control;
public ResizeCommand(int width, int height, Control control)
{
_changeOfWidth = width;
_changeOfHeight = height;
_control = control;
}
public void Execute()
{
_control.Height = _control.Height + _changeOfHeight;
_control.Width = _control.Width + _changeOfWidth;
}
public void UnExecute()
{
_control.Height = _control.Height - _changeOfHeight;
_control.Width = _control.Width - _changeOfWidth;
}
}
Ellipse.cs
class Ellipse : Control
{
private Point mDown { get; set; }
private Point conLoc { get; set; }
private bool userResizing = false;
private Form1 form =null;
public Ellipse(Form1 form1)
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
this.ResizeRedraw = true;
this.form = form1;
}
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;
conLoc = this.Location;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (this.Location != conLoc)
{
Console.WriteLine("You moved me");
this.form.InsertInUnDoRedoForMove(conLoc.Y, conLoc.X, this);
}
}
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;
const int WM_EXITSIZEMOVE = 0x232;
const int WM_NCLBUTTONDWN = 0xA1;
if (m.Msg == WM_NCLBUTTONDWN)
{
if (!userResizing)
{
userResizing = true;
Console.WriteLine("Start Resizing");
this.form.InsertInUnDoRedoForResize(this.Width, this.Height, this);
}
}
else if (m.Msg == WM_EXITSIZEMOVE)
{
if (userResizing)
{
userResizing = false;
Console.WriteLine("Finish Resizing");
}
}
else 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);
}
}
Form1.cs的
public partial class Form1 : Form
{
private bool draw;
private int x, y, xe, ye;
private Stack<ICommand> _Undocommands = new Stack<ICommand>();
private Stack<ICommand> _Redocommands = new Stack<ICommand>();
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(this);
panel.Controls.Add(box);
box.Location = new Point(x, y);
box.Width = (xe - x);
box.Height = (ye - y);
//InsertCommand boxcmd = new InsertCommand(box, panel);
InsertInUnDoRedoForInsert(box);
break;
case Item.Ellipse:
var el = new Ellipse(this);
panel.Controls.Add(el);
el.Location = new Point(x, y);
el.Width = (xe - x);
el.Height = (ye - y);
//InsertCommand elcmd = new InsertCommand(el,panel);
InsertInUnDoRedoForInsert(el);
break;
default:
break;
}
}
private void undoButton_Click(object sender, EventArgs e)
{
Undo(1);
}
private void redoButton_Click(object sender, EventArgs e)
{
Redo(1);
}
private void clearAllButton_Click(object sender, EventArgs e)
{
commandList.Clear();
current = 0;
panel.Controls.Clear();
}
public void Redo(int levels)
{
for (int i = 1; i <= levels; i++)
{
if (_Redocommands.Count != 0)
{
ICommand command = _Redocommands.Pop();
command.Execute();
_Undocommands.Push(command);
}
Invalidate();
}
}
public void Undo(int levels)
{
for (int i = 1; i <= levels; i++)
{
if (_Undocommands.Count != 0)
{
ICommand command = _Undocommands.Pop();
command.UnExecute();
_Redocommands.Push(command);
}
Invalidate();
}
}
#region UndoHelperFunctions
public void InsertInUnDoRedoForInsert(Control control)
{
ICommand cmd = new InsertCommand(control, panel,shapeTreeView);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
public void InsertInUnDoRedoForResize
(int width, int height, Control control)
{
ICommand cmd = new ResizeCommand(width, height, control);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
public void InsertInUnDoRedoForMove
(int top, int left, Control control)
{
ICommand cmd = new MoveCommand(top,left,control);
_Undocommands.Push(cmd); _Redocommands.Clear();
}
#endregion
}