Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow pre/post changeset actions? #3

Open
nathanaw opened this issue Aug 31, 2017 · 1 comment
Open

Allow pre/post changeset actions? #3

nathanaw opened this issue Aug 31, 2017 · 1 comment
Assignees

Comments

@nathanaw
Copy link
Owner

(Submitted by ses4j on Codeplex)

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)
                    this.PreChangeAction();

                if (null == root)
                    return;

                _UndoRoot = root;
                this.PreChangeAction = preChangeAction;
                this.PostChangeAction = postChangeAction;
                this.AddInitialAction();

                root.BeginChangeSetBatch(description, consolidateChangesForSameInstance);
            }

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

            private void AddInitialAction()
            {
                object target = null;
                var changeToRebindUponUndo = new DelegateChange(target,
                                this.PostChangeAction,
                                this.PreChangeAction,
                                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,
                                this.PreChangeAction,
                                this.PostChangeAction,
                                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)
                    {
                        this.AddFinalAction();
                        _UndoRoot.EndChangeSetBatch();
                    }

                    if (this.PostChangeAction != null)
                        this.PostChangeAction();
                }
            }

            /// <summary>
            /// Disposing this instance will end the associated Undo batch.
            /// </summary>
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            #endregion
        }

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?

Thanks,
Nathan


ses4j wrote Feb 22, 2014 at 6:12 PM [x]
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!
Scott


nallenwagner wrote Feb 22, 2014 at 9:32 PM [x]
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.

Nathan


ses4j wrote Feb 22, 2014 at 10:03 PM [x]
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?

@nathanaw nathanaw self-assigned this Apr 18, 2020
@nathanaw
Copy link
Owner Author

Will think on this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant