Domain model + Undo framework

May 14, 2010 at 12:41 PM

Dear Kirill,

Thanks for sharing your framework with us. It's well documented and easy to use.

I've started to play with it and I've found one confusing thing in documentation:


 /// <summary>
    /// Action Manager is a central class for the Undo Framework.
    /// Your domain model (business objects) will have an ActionManager reference that would 
    /// take care of executing actions.
    /// Here's how it works:
    /// 1. You declare a class that implements IAction
    /// 2. You create an instance of it and give it all necessary info that it needs to know
    ///    to apply or rollback a change
    /// 3. You call ActionManager.RecordAction(yourAction)
    /// Then you can also call ActionManager.Undo() or ActionManager.Redo()
    /// </summary>
    public class ActionManager


As I understand my domain model in MVVM pattern is my ViewModel/DataModel, e.g.:

 public class Person:INotifyPropertyChanged
        private string firstName;

        public string FirstName
            get { return firstName; }
                if (value == firstName) return;

        private void SetValue(string propertyName,string newValue)
            var undo = new PropertyChangedUndoEvent(propertyName, firstName, newValue, this);

I can't figure out how to implement PropertyChangedUndoEvent to avoid throwing of an exception from ActionManager.

"ActionManager.RecordActionDirectly: the ActionManager is currently running or undoing an action (App.PropertyChangedUndoEvent), and this action (while being executed) attempted to recursively record another action"

My current implementation is:

 public class PropertyChangedUndoEvent:AbstractAction
        private string propertyName;
        private string oldValue;
        private string newValue;
        private object target;
        private bool reentrencyGuard;
        public PropertyChangedUndoEvent(string propertyName,string oldValue,string newValue,object target)
            this.propertyName = propertyName;
            this.oldValue = oldValue;
            this.newValue = newValue;
   = target;
            AllowToMergeWithPrevious = true;

        private void SetValue(string value)
            var property = target.GetType().GetProperty(propertyName);
            property.SetValue(target, value,null);

        protected override void ExecuteCore()

        protected override void UnExecuteCore()
Kirill, can you help me to deal with the problem, please?

Yours sincerely,

Maxim Filimonov

May 14, 2010 at 4:10 PM

Maxim, what happens is that you have a loop: calling the FirstName setter calls RecordAction which in turn calls FirstName setter again using Reflection - this will be an infinite loop, but the Undo manager detects that and gives you a warning.
Instead, you should try either:
        private void SetValue(string value)
            var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            field.SetValue(target, value, null);
Or have another property like FirstNameInternal, and call SetValue("FirstNameInternal", value); from within FirstName setter. This will break the loop.
Hope this helps,

May 14, 2010 at 4:16 PM
Dear Kirill, Thanks for your response. I will try your suggestion I'm also thinking about forwarding undo framework to underlying model which will also break the loop. Yours sincerely, Maxim