I have successfully used the UndoManager framework before in a PivotGrid which I thought would be very difficult. So, I am pretty familiar with the UndoManager already. However, I am having trouble undoing all. I cannot use transactions to log these changes so I am just relying on the following loop to do this for me:
UndoMgr.Suspend();
while (UndoMgr.CanUndo)
{ //UndoMgr.PreventMerge(); UndoMgr.Undo(); } UndoMgr.ClearHistory(); UndoMgr.Resume();
I tried this both with the suspend call at the beginning and without. The PreventMerge() sounded promising but also didn't work. If I have Suspend in there, I get an System.InvalidOperationException. I guess I can't Undo while suspended. If I take out the call to Suspend, then each Undo operation is logged as an undoable transaction and I get double the number of undos that I want and I end up right back where I was with the undos having no effect.
What I really want is a function UndoManager.UndoAll() that just undoes everything on the Undo list and nothing more. Is that possible? It seems like I've seen samples where you can undo N operations at once (not using transactions). Is there a way to undo all on the list and the just clear the history in a clean way.
Thanks!
Sorry but I'm still not quite following. The UndoUnit is not automatically added to the Undo/Redo history. Typically what happens is that you would make some change in your object model (e.g. you type into a textbox that is bound to your property) and that object model would record the change (e.g. via the AddPropertyChange). Since the UndoManager is not in an Undo or Redo operation that operation is added to the Undo history (and the Redo history is cleared as typically happens in a linear undo functionality). When you invoke the Undo method of the undo manager, it pops the top item off the Undo history and just calls the Execute method of the UndoUnit on the Undo history (so in this case the Execute of the AddPropertyChange) which would call into the setter of your property reverting the value to the previous value; it does not add that UndoUnit to the Redo history. Assuming that you are calling AddPropertyChange from the property setter that would then call AddPropertyChange. Since the UndoManager is in an Undo operation the UndoUnit would get added to the Redo history. So you should not end up with double items in the Undo/Redo history. What you would have in this case is 1 item on the Redo history that represents the operation that allows for the reverse of the undo operation (i.e. to once again perform the original action that was added to the Undo history). Again this would be typical for linear undo functionality. If you really are getting double items in the Undo or Redo history then it sounds like you might be calling the AddXXXChange methods multiple times during a single execution. If you need us to look at it then please provide a project that demonstrates the issue.
I saw your next reply and yes if you wanted to avoid adding items to the Redo history while performing an Undo then you could check the IsPerformingUndo, etc. and not call AddPropertyChange but then your client would never be able to perform a Redo.
I think I see what I'm doing wrong... I decided I should check the IsPerformingUndo/Redo/Rollback before calling AddPropertyChange in my setter functions. Sound right?
What I mean by getting double undos is that my Undo actions are getting logged with the Undo manager again my undo actually gets added back to the list. Here's what I mean. When a property changes that I want to undo, I log changes to the property with a call to AddPropertyChange(). Great. This adds the change to the list of undo actions. However, when I call Undo() again my property setter gets called and again calls AddPropertyChange() this time logging the undo.
Then the second time through my loop above, the undo manager still has pending undo actions (the last being the undo that was just applied) and then will undo my undo. Does any of that make sense?
Am I doing something fundamentally wrong when logging the undo actions in the first place?
This would be a whole lot easier if there was an API on Undo that could just UndoAll() and not log any of those undos in the undo history. Or at least support Undo operations while in suspended mode.
Undo/redo are not allowed while suspended. I'm not sure what you mean by double the items.
To undo all and clear the history you could do something like but your while loop would essentially do the same thing:
if (mgr.TopUndoHistoryItem != null) { mgr.UndoHistory.Last().Execute(); mgr.ClearHistory(); }
{
mgr.UndoHistory.Last().Execute();
mgr.ClearHistory();
}