Allow pre/post changeset actions?


I have run into a couple situations where I need to run an action after a change is applied, either undoing or redoing. In this case, it's to simply "refresh" a view after a change, because trying to do it step-by-step is too complex. There is no obvious way to do it, but I built my own UndoBatch class that seems to work well.

What do you think? Should I send it as a pull request, possibly replacing the existing UndoBatch? Is there a preferred way?

Here it is:
        public class UndoBatchWithAction : IDisposable
            public UndoBatchWithAction(ISupportsUndo instance, string description, bool consolidateChangesForSameInstance, Action preChangeAction = null, Action postChangeAction = null)
                : this(UndoService.Current[instance.GetUndoRoot()], description, consolidateChangesForSameInstance, preChangeAction, postChangeAction)

            public UndoBatchWithAction(UndoRoot root, string description, bool consolidateChangesForSameInstance, Action preChangeAction = null, Action postChangeAction = null)
                if (this.PreChangeAction != null)

                if (null == root)

                _UndoRoot = root;
                this.PreChangeAction = preChangeAction;
                this.PostChangeAction = postChangeAction;

                root.BeginChangeSetBatch(description, consolidateChangesForSameInstance);

            private UndoRoot _UndoRoot;
            private Action PostChangeAction;
            private Action PreChangeAction;

            private void AddInitialAction()
                object target = null;
                var changeToRebindUponUndo = new DelegateChange(target,
                                new Tuple<object, string>(target, "Initial action"));

                this._UndoRoot.AddChange(changeToRebindUponUndo, "Act before changeset is applied.");

            private void AddFinalAction()
                object target = null;
                var changeToRebindUponRedo = new DelegateChange(target,
                                new Tuple<object, string>(target, "Final action."));

                this._UndoRoot.AddChange(changeToRebindUponRedo, "Act after changeset is applied.");

            #region IDisposable Members

            private void Dispose(bool disposing)
                if (disposing)
                    if (null != _UndoRoot)

                    if (this.PostChangeAction != null)

            /// <summary>
            /// Disposing this instance will end the associated Undo batch.
            /// </summary>
            public void Dispose()



nallenwagner wrote Feb 21, 2014 at 9:09 PM

Hi @ses4j,

Thanks for the suggestion here.

I have two thoughts, but maybe these don't meet your exact scenario...

1 - UndoRoot has two events on it that should fire any time an undo or a redo happen. If you hook these events, you could do the refresh as a response.

2 - The Change class will inspect the "target" of the undo to see if it implements ISupportUndoNotification. If so, it'll call UndoHappened or RedoHappened after applying the changes to that object.

I suspect that your "refresh" needs to touch something outside your objects that are being undone. If so, then the UndoRoot might be the best option. Does this make sense and/or meet your goals? If not, can you help me understand more about your scenario?


ses4j wrote Feb 22, 2014 at 10:12 PM

If I understand correctly, neither of those hooks are convenient for me because I don't want it to happen on ALL undos or even all undos to a particular class. It's really just one particular change section.

In my case, that change is about radically resetting the data context of a WPF data grid on a file load-type operation. I want the whole load batched into a single undo operation, and I want the grid to rebind after the entire change occurs (or de-occurs).

Thanks for the reply!

nallenwagner wrote Feb 23, 2014 at 1:32 AM

Thanks @ses4j. That makes sense.

I think what you've put together here looks like a great solution. Thanks for sharing it. I'll see what I can do to include it in future releases of the library, if you'd like.


ses4j wrote Feb 23, 2014 at 2:03 AM

Sure. Like I said, happy to make a pull request if you want. Would you prefer it as simply a patch to UndoBatch with additional optional arguments, or as a new standalone class?