积极答复者
WPF程序怎么实现实现Redo/Undo (即重做/撤消)功能?

问题
-
WPF程序怎么实现Redo/Undo (即重做/撤消)功能?
WPF程序是一个InkCanvas画布涂鸦的功能,其在上面有涂鸦,添加图片,视频,删除,选择,擦除,移动,放大缩小等操作,需要一个Redo/Undo (即重做/撤消)功能,来管理InkCanvas上面的编辑操作,,用什么方法好我网上看了一些:比如:
1.应用Infragistics 框架:http://www.infragistics.com/community/blogs/ambrose_little/archive/2012/01/26/infragistics-undo-redo-framework-ctp.aspx
2.http://www.cnblogs.com/nankezhishi/archive/2008/11/18/1336211.html
根据我的画布涂鸦程序的Redo/Undo (即重做/撤消)功能,使用什么方法比较好,又比较简单,能够满足这几个操作的功能就行了,能否有一些Demo查看??
答案
全部回复
-
我这里有一个InkCanvas 的Redo/Undo (即重做/撤消)功能,但是他只是对stroke进行Redo/Undo (即重做/撤消),我的Inkcanvas上还有图片,视频的控件,怎么修改呢??
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Ink; namespace InkCanvasEditingModes { sealed class CommandStack { /// <summary> /// Initialization. /// </summary> /// <param name="strokes"></param> public CommandStack(StrokeCollection strokes) { if (strokes == null) { throw new ArgumentNullException("strokes"); } _strokeCollection = strokes; _undoStack = new Stack<CommandItem>(); _redoStack = new Stack<CommandItem>(); _disableChangeTracking = false; } /// <summary> /// StrokeCollection to track changes for /// </summary> public StrokeCollection StrokeCollection { get { return _strokeCollection; } } /// <summary> /// Only undo if there are more items in the stack to step back into. /// </summary> public bool CanUndo { get { return (_undoStack.Count > 0); } } /// <summary> /// Only undo if one or more steps back in the stack. /// </summary> public bool CanRedo { get { return (_redoStack.Count > 0); } } /// <summary> /// Add an item to the top of the command stack /// </summary> public void Undo() { if (!CanUndo) throw new InvalidOperationException("No actions to undo"); CommandItem item = _undoStack.Pop(); // Invoke the undo operation, with change-tracking temporarily suspended. _disableChangeTracking = true; try { item.Undo(); } finally { _disableChangeTracking = false; } //place this item on the redo stack _redoStack.Push(item); } /// <summary> /// Take the top item off the command stack. /// </summary> public void Redo() { if (!CanRedo) throw new InvalidOperationException(); CommandItem item = _redoStack.Pop(); // Invoke the redo operation, with change-tracking temporarily suspended. _disableChangeTracking = true; try { item.Redo(); } finally { _disableChangeTracking = false; } //place this item on the undo stack _undoStack.Push(item); } /// <summary> /// Add a command item to the stack. /// </summary> /// <param name="item"></param> public void Enqueue(CommandItem item) { if (item == null) { throw new ArgumentNullException("item"); } // Ensure we don't enqueue new items if we're being changed programmatically. if (_disableChangeTracking) { return; } // Check to see if this new item can be merged with previous. bool merged = false; if (_undoStack.Count > 0) { CommandItem prev = _undoStack.Peek(); merged = prev.Merge(item); } // If not, append the new command item if (!merged) { _undoStack.Push(item); } //clear the redo stack if (_redoStack.Count > 0) { _redoStack.Clear(); } } /// <summary> /// Implementation /// </summary> private StrokeCollection _strokeCollection; private Stack<CommandItem> _undoStack; private Stack<CommandItem> _redoStack; bool _disableChangeTracking; // reentrancy guard: disables tracking of programmatic changes // (eg, in response to undo/redo ops) } /// <summary> /// Derive from this class for every undoable/redoable operation you wish to support. /// </summary> abstract class CommandItem { public static Guid STROKE_INDEX_PROPERTY = Guid.NewGuid(); // Interface public abstract void Undo(); public abstract void Redo(); // Allows multiple subsequent commands of the same type to roll-up into one // logical undoable/redoable command -- return false if newitem is incompatable. public abstract bool Merge(CommandItem newitem); // Implementation protected CommandStack _commandStack; protected CommandItem(CommandStack commandStack) { _commandStack = commandStack; } } /// <summary> /// This operation covers collecting new strokes, stroke-erase, and point-erase. /// </summary> class StrokesAddedOrRemovedCI : CommandItem { InkCanvasEditingMode _editingMode; StrokeCollection _added, _removed; int _editingOperationCount; public StrokesAddedOrRemovedCI(CommandStack commandStack, InkCanvasEditingMode editingMode, StrokeCollection added, StrokeCollection removed, int editingOperationCount) : base(commandStack) { _editingMode = editingMode; _added = added; _removed = removed; _editingOperationCount = editingOperationCount; } public override void Undo() { _commandStack.StrokeCollection.Remove(_added); //_commandStack.StrokeCollection.Add(_removed); AddAllStrokesAtOriginalIndex(_removed); } private void AddAllStrokesAtOriginalIndex(StrokeCollection toBeAdded) { foreach (Stroke stroke in toBeAdded) { int strokeIndex = (int)stroke.GetPropertyData(STROKE_INDEX_PROPERTY); if (strokeIndex > _commandStack.StrokeCollection.Count) { strokeIndex = _commandStack.StrokeCollection.Count; } _commandStack.StrokeCollection.Insert(strokeIndex, stroke); } } public override void Redo() { //_commandStack.StrokeCollection.Add(_added); AddAllStrokesAtOriginalIndex(_added); _commandStack.StrokeCollection.Remove(_removed); } public override bool Merge(CommandItem newitem) { StrokesAddedOrRemovedCI newitemx = newitem as StrokesAddedOrRemovedCI; if (newitemx == null || newitemx._editingMode != _editingMode || newitemx._editingOperationCount != _editingOperationCount) { return false; } // We only implement merging for repeated point-erase operations. if (_editingMode != InkCanvasEditingMode.EraseByPoint) return false; if (newitemx._editingMode != InkCanvasEditingMode.EraseByPoint) return false; // Note: possible for point-erase to have hit intersection of >1 strokes! // For each newly hit stroke, merge results into this command item. foreach (Stroke doomed in newitemx._removed) { if (_added.Contains(doomed)) { _added.Remove(doomed); } else { _removed.Add(doomed); } } _added.Add(newitemx._added); return true; } } /// <summary> /// This operation covers move and resize operations. /// </summary> class SelectionMovedOrResizedCI : CommandItem { StrokeCollection _selection; Rect _newrect, _oldrect; int _editingOperationCount; public SelectionMovedOrResizedCI(CommandStack commandStack, StrokeCollection selection, Rect newrect, Rect oldrect, int editingOperationCount) : base(commandStack) { _selection = selection; _newrect = newrect; _oldrect = oldrect; _editingOperationCount = editingOperationCount; } public override void Undo() { Matrix m = GetTransformFromRectToRect(_newrect, _oldrect); _selection.Transform(m, false); } public override void Redo() { Matrix m = GetTransformFromRectToRect(_oldrect, _newrect); _selection.Transform(m, false); } public override bool Merge(CommandItem newitem) { SelectionMovedOrResizedCI newitemx = newitem as SelectionMovedOrResizedCI; // Ensure items are of the same type. if (newitemx == null || newitemx._editingOperationCount != _editingOperationCount || !StrokeCollectionsAreEqual(newitemx._selection, _selection)) { return false; } // Keep former oldrect, latter newrect. _newrect = newitemx._newrect; return true; } static Matrix GetTransformFromRectToRect(Rect src, Rect dst) { Matrix m = Matrix.Identity; m.Translate(-src.X, -src.Y); m.Scale(dst.Width / src.Width, dst.Height / src.Height); m.Translate(+dst.X, +dst.Y); return m; } static bool StrokeCollectionsAreEqual(StrokeCollection a, StrokeCollection b) { if (a == null && b == null) return true; if (a == null || b == null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; ++i) if (a[i] != b[i]) return false; return true; } } }
-