Coder Social home page Coder Social logo

nathanaw / muf Goto Github PK

View Code? Open in Web Editor NEW
35.0 1.0 12.0 1.41 MB

Monitored Undo Framework - A simple undo/redo framework for .NET

Home Page: https://nathan.alner.net/2010/10/13/wpf-amp-entity-framework-4-tales-from-the-trenches/

License: MIT License

C# 100.00%
undo-framework redo-framework wpf-applications nuget muf undo undo-redo

muf's People

Contributors

nathanaw avatar thisisconsilium avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

muf's Issues

Limit UndoStack Size

(Submitted by droberts75 on codeplex)

Can we add a limit to the undostack so if a user is using the applications all day long we don't build a stack with thousands of changes? Something like 50 or 100 would be ideal.

Allow pre/post changeset actions?

(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?

Problems on changes from outside the UI thread: System.InvalidOperationException

Not so much of an issue as a warning to others. If you have a need to load your monitored document in the background from a BackgroundWorker, you may run into a sporadic error.

"System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'"

the cure for this is to make sure you are doing all your operations in the UI thread. Or It may be that the MUF code could be changed to make sure that all operations occur on the UI thread.

Memory Leak

(Submitted by jrvdboom on Codeplex)

Despite the UndoRoot containing a WeakReference to its root object, memory is still leaked by the _Roots dictionary of the UndoService, because its keys are the same root objects but then strong-referenced. Adding a RemoveRoot method or changing the dictionary to use WeakReference keys can solve the problem.

[Q] Any simple usage of Collection undo/redo?

I'm trying to create a system to redo/undo with the whole canvas elements of my program with muf.
canvas elements is bound to ObservableCollection<FrameworkElement> and everytime user add/remove the element, it goes to the property

public CustomCollection<FrameworkElement> m_CanvasElements = new CustomCollection<FrameworkElement>();
    public CustomCollection<FrameworkElement> CanvasElements
    {
        get {
            return m_CanvasElements; }
        set {

            m_CanvasElements = value;
            OnPropertyChanged("CanvasElements");
        }
    }

And here's what I've tried to do. I made an inherited class of ObservableCollection to use an NotifyCollectionChangedEventArgs as a parameter.

public class CustomCollection<T> : ObservableCollection<T>, ICloneable
    {
        public event EventHandler<NotifyCollectionChangedEventArgs> ItemAdded;

        public CustomCollection()
        {
            CollectionChanged += MyObservableCollection_CollectionChanged;
        }

        void MyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {

            DefaultChangeFactory.Current.OnCollectionChanged(this, "CanvasElements", MainWindow.VM.m_CanvasElements, e);
         ...
        }

I have tried both OnCollectionChanged and GetCollectionChange. and it seems doesn't work.
Currently, OnChanging is working exactly that I expected.
Any ideas?

example of batch changes

Hi, cannot fully understand the muf-s batch concept. the properties are mostly changed by other classes, and there are scenarios when different properties are changed in one batch set. Can not figure out how to start batching mode , and stop it when it needed.

UndoBatch doesn't allow for cancellation of batch

The documentation says that UndoBatch is designed similarly to TransactionScope, but TransactionScope requires a "ts.Complete()" call to commit, otherwise the change is rolled back. In the current UndoBatch, there's no ability to rollback. Conforming to the TransactionScope pattern, or at least providing some kind of .Rollback() would be very useful.


I worked around it with

bool success = true;
using (new UndoBatch(...)) {
    ...
}

if (!success) {
    var root = UndoService.Current[undoroot];
    root.Undo();
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.