Today I've discovered that the FW 4.5 has their own undoredo manager (if I understood good) http://msdn.microsoft.com/en-us/library/System.ComponentModel.Design.UndoEngine%28v=vs.110%29.aspx
Well, I can't find any example about how to start using this class just to make a simple undo/redo of a text based control, I know other alternatives to do undoable things, but just I want to learn how to use this.
When I try to use the constructor it has any parameter to be passed, and also the Intellisense does not shows me any method for the System.ComponentModel.Design.UndoEngine
class, really I don't know how I could use it.
Someone could illustrate ours with an example for C#
or VBNET
? (I preffer VBNET documentation if possibly, please)
UndoEngine is an abstract class, Visual Studio and Designers have implemented UndoEngine in their own way, and those must be private or not available for redistribution. You will not be able to use it, in fact abstract class is just an interface with little implementation, it is not at all an undo framework.
You still have to write your own undo management, however benefit of deriving your undo engine from UndoEngine class is, it can be easily hosted/integrated with VS and other MS based editors.
The simplest UndoEngine Implementation
Let us say you are changing a global variable,
// following code uses big UndoUnit
public void SetGlobalVariable(object v){
var oldValue = GlobalVariable;
GlobalVariable = v;
var action = new UndoUnit{
UndoAction = ()=>{
GlobalVariable = oldValue;
},
RedoAction = ()=>{
GlobalVariable = v;
}
};
UndoManager.Add(action);
}
/// <summary>
/// Used to indicates the designer's status
/// </summary>
public enum UndoUnitState
{
Undoing,
Redoing,
}
/// <summary>
/// A UndoUnitBase can be used as a IOleUndoUnit or just a undo step in
/// a transaction
/// </summary>
public class UndoUnitBase : IOleUndoUnit
{
public Action UndoAction {get;set;}
public Action RedoAction {get;set;}
private string name = null;
private Guid clsid = Guid.Empty;
private bool inDoAction = false;
private bool isStillAtTop = true;
private UndoUnitState unitState = UndoUnitState.Undoing;
protected UndoUnit UnitState
{
get { return unitState; }
set { unitState = value; }
}
/// <summary>
/// </summary>
/// <param name="undoManager"></param>
/// <param name="name"></param>
internal UndoUnitBase(string name)
{
this.name = name;
}
~UndoUnitBase()
{
}
/// <summary>
/// </summary>
protected bool InDoAction
{
get
{
return inDoAction;
}
}
public string UndoName
{
get
{
return name;
}
set
{
this.name = value;
}
}
public Guid Clsid
{
get { return clsid; }
set { clsid = value; }
}
/// <summary>
/// This property indicates whether the undo unit is at the top (most recently added to)
/// the undo stack. This is useful to know when deciding whether undo units for operations
/// like typing can be coallesced together.
/// </summary>
public bool IsStillAtTop
{
get { return isStillAtTop; }
}
/// <summary>
/// This function do the actual undo, and then revert the action to be a redo
/// </summary>
/// <returns>objects that should be selected after DoAction</returns>
protected abstract void DoActionInternal();
/// <devdoc>
/// IOleUndoManager's "Do" action.
/// </devdoc>
void IOleUndoUnit.Do(IOleUndoManager oleUndoManager)
{
Do(oleUndoManager);
}
protected virtual int Do(IOleUndoManager oleUndoManager)
{
try
{
if(unitState== UndoUnitState.Undoing){
UndoAction();
}else{
RedoAction();
}
unitState = (unitState == UndoUnitState.Undoing) ? UndoUnitState.Redoing : UndoUnitState.Undoing;
if (oleUndoManager != null)
oleUndoManager.Add(this);
return VSConstants.S_OK;
}
catch (COMException e)
{
return e.ErrorCode;
}
catch
{
return VSConstants.E_ABORT;
}
finally
{
}
}
/// <summary>
/// </summary>
/// <returns></returns>
void IOleUndoUnit.GetDescription(out string pBstr)
{
pBstr = name;
}
/// <summary>
/// </summary>
/// <param name="clsid"></param>
/// <param name="pID"></param>
/// <returns></returns>
void IOleUndoUnit.GetUnitType(out Guid pClsid, out int plID)
{
pClsid = Clsid;
plID = 0;
}
/// <summary>
/// </summary>
void IOleUndoUnit.OnNextAdd()
{
// We are no longer the top most undo unit; another one was added.
isStillAtTop = false;
}
}
public class MyUndoEngine : UndoEngine, IUndoHandler
{
Stack<UndoEngine.UndoUnit> undoStack = new Stack<UndoEngine.UndoUnit>();
Stack<UndoEngine.UndoUnit> redoStack = new Stack<UndoEngine.UndoUnit>();
public ReportDesignerUndoEngine(IServiceProvider provider) : base(provider)
{
}
#region IUndoHandler
public bool EnableUndo {
get {
return undoStack.Count > 0;
}
}
public bool EnableRedo {
get {
return redoStack.Count > 0;
}
}
public void Undo()
{
if (undoStack.Count > 0) {
UndoEngine.UndoUnit unit = undoStack.Pop();
unit.Undo();
redoStack.Push(unit);
}
}
public void Redo()
{
if (redoStack.Count > 0) {
UndoEngine.UndoUnit unit = redoStack.Pop();
unit.Undo();
undoStack.Push(unit);
}
}
#endregion
protected override void AddUndoUnit(UndoEngine.UndoUnit unit)
{
undoStack.Push(unit);
}
}
If your question is how to use it at runtime, then the answer is in MSDN:
Specifies generic undo/redo functionality at design time.
So I doubt that it is easily usable at runtime.
If you meant an example of custom user control utilizing this class, I can't find any, sorry.
Find an implementation of the UndoEngine and how to use it right here: https://github.com/icsharpcode/SharpDevelop/search?q=ReportDesignerUndoEngine&ref=cmdform
HTH
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With