1. redo和undo的实现
知道执行了什么命令,影响了那些数据
终止条件和状态
2. 这里我犯了个错误,其实我根本不需要知道是执行的什么命令,对于绘图系统,我只需知道对数据产生了那些影响。撤销和重做其实都是针对数据(几何对象)来说的。
所有这里只需要在操作(添加几何对象、删除几何对象、修改几何对象、增加节点、删除节点、移动节点等)时另外引一条线,就是UndoRedoBuffer对象,他是一个List,其实如果是栈则更好,用它记录操作影响的数据。注意是操作影响的数据,和上面所说的操作是两个概念。
抽象了一个操作影响数据的EditCommandBase类,具体子类有:添加影响数据类,删除影响数据类、移动影响数据类。
在每一次修改Canvas对象的几何对象集合时都要引出一条线,记录这个操作影响数据信息,即加入UndoRedoBuffer。
Undo就是UndoRedoBuffer顶部的影响数据修改几何对象集合,实现回退。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace Canvas
{
class EditCommandBase
{
public virtual bool DoUndo(IModel data)
{
return false;
}
public virtual bool DoRedo(IModel data)
{
return false;
}
}
class EditCommandAdd : EditCommandBase
{
List<IDrawObject> m_objects = null;
IDrawObject m_object;
ICanvasLayer m_layer;
public EditCommandAdd(ICanvasLayer layer, IDrawObject obj)
{
m_object = obj;
m_layer = layer;
}
public EditCommandAdd(ICanvasLayer layer, List<IDrawObject> objects)
{
m_objects = new List<IDrawObject>(objects);
m_layer = layer;
}
public override bool DoUndo(IModel data)
{
if (m_object != null)
data.DeleteObjects(new IDrawObject[] { m_object });
if (m_objects != null)
data.DeleteObjects(m_objects);
return true;
}
public override bool DoRedo(IModel data)
{
if (m_object != null)
data.AddObject(m_layer, m_object);
if (m_objects != null)
{
foreach (IDrawObject obj in m_objects)
data.AddObject(m_layer, obj);
}
return true;
}
}
class EditCommandRemove : EditCommandBase
{
Dictionary<ICanvasLayer, List<IDrawObject>> m_objects = new Dictionary<ICanvasLayer, List<IDrawObject>>();
public EditCommandRemove()
{
}
public void AddLayerObjects(ICanvasLayer layer, List<IDrawObject> objects)
{
m_objects.Add(layer, objects);
}
public override bool DoUndo(IModel data)
{
foreach (ICanvasLayer layer in m_objects.Keys)
{
foreach (IDrawObject obj in m_objects[layer])
data.AddObject(layer, obj);
}
return true;
}
public override bool DoRedo(IModel data)
{
foreach (ICanvasLayer layer in m_objects.Keys)
data.DeleteObjects(m_objects[layer]);
return true;
}
}
class EditCommandMove : EditCommandBase
{
List<IDrawObject> m_objects = new List<IDrawObject>();
UnitPoint m_offset;
public EditCommandMove(UnitPoint offset, IEnumerable<IDrawObject> objects)
{
m_objects = new List<IDrawObject>(objects);
m_offset = offset;
}
public override bool DoUndo(IModel data)
{
foreach (IDrawObject obj in m_objects)
{
UnitPoint offset = new UnitPoint(-m_offset.X, -m_offset.Y);
obj.Move(offset);
}
return true;
}
public override bool DoRedo(IModel data)
{
foreach (IDrawObject obj in m_objects)
obj.Move(m_offset);
return true;
}
}
class EditCommandNodeMove : EditCommandBase
{
List<INodePoint> m_objects = new List<INodePoint>();
public EditCommandNodeMove(IEnumerable<INodePoint> objects)
{
m_objects = new List<INodePoint>(objects);
}
public override bool DoUndo(IModel data)
{
foreach (INodePoint obj in m_objects)
obj.Undo();
return true;
}
public override bool DoRedo(IModel data)
{
foreach (INodePoint obj in m_objects)
obj.Redo();
return true;
}
}
class EditCommandEditTool : EditCommandBase
{
IEditTool m_tool;
public EditCommandEditTool(IEditTool tool)
{
m_tool = tool;
}
public override bool DoUndo(IModel data)
{
m_tool.Undo();
return true;
}
public override bool DoRedo(IModel data)
{
m_tool.Redo();
return true;
}
}
class UndoRedoBuffer
{
List<EditCommandBase> m_undoBuffer = new List<EditCommandBase>();
List<EditCommandBase> m_redoBuffer = new List<EditCommandBase>();
bool m_canCapture = true;
bool m_dirty = false;
public UndoRedoBuffer()
{
}
public void Clear()
{
m_undoBuffer.Clear();
m_redoBuffer.Clear();
}
public bool Dirty
{
get { return m_dirty; }
set { m_dirty = value;}
}
public bool CanCapture
{
get { return m_canCapture; }
}
public bool CanUndo
{
get { return m_undoBuffer.Count > 0; }
}
public bool CanRedo
{
get { return m_redoBuffer.Count > 0; }
}
public void AddCommand(EditCommandBase command)
{
if (m_canCapture && command != null)
{
m_undoBuffer.Add(command);
m_redoBuffer.Clear();
Dirty = true;
}
}
public bool DoUndo(IModel data)
{
if (m_undoBuffer.Count == 0)
return false;
m_canCapture = false;
EditCommandBase command = m_undoBuffer[m_undoBuffer.Count - 1];
bool result = command.DoUndo(data);
m_undoBuffer.RemoveAt(m_undoBuffer.Count - 1);
m_redoBuffer.Add(command);
m_canCapture = true;
Dirty = true;
return result;
}
public bool DoRedo(IModel data)
{
if (m_redoBuffer.Count == 0)
return false;
m_canCapture = false;
EditCommandBase command = m_redoBuffer[m_redoBuffer.Count - 1];
bool result = command.DoRedo(data);
m_redoBuffer.RemoveAt(m_redoBuffer.Count - 1);
m_undoBuffer.Add(command);
m_canCapture = true;
Dirty = true;
return result;
}
}
}