In Object Oriented world what needs to be done if a users accidentally changes the value of a property? Very often users make a mistake that needs to be cancelled out. The situation is more problematic if the object state has been persisted. In order to make our objects more user friendly, we need to provide a mechanism of un-doing the changes. This article provides one such implementation of the undo/ redo capability. The code represented is in C#.
Basically, if we look at the undo capability of other applications – like word, we notice that the undo functionality is implemented as a Last In – First Out mechanism. This obviously calls for a Stack based implementation.
To understand a stack, think about a long tube with the diameter just about the size of a tennis ball. Also assume that this pipe is closed at one end. So if one puts a tennis balls inside in such a tube, one after another, then what happens if want to take out these balls? It should be clear that these balls can only be taken out in the reverse order of insertion – i.e. the last ball inserted would be the first to be taken out. For more detail, refer to Stack class in the
So essentially what need to do is to somehow push the changes to the object’s state in a stack and then in the event of an undo operation, pop the last entry off this stack and revert back the changes. Now, the next problem to tackle is of recording the state information. An object may have multiple properties and hence any of these may change. We would therefore need a generic implementation that could work with all objects
In order to record the state information, we need to define a class that will store the value of a property prior to it being changed. Now, since we are building a generic concept, we cannot type cast the value. The property type can be any thing –
double etc. So when we define our
PropertyValue class, we would declare the value as object. The main reason for this is that .NET automatically boxes intrinsic data types to objects of corresponding type and hence the developer does not have to spend time in doing the type conversion.
Therefore our class structure may look like this.
PropertyName property would store the name of the property whose state is being preserved. The
PropertyValue property would store the original state prior to the change being applied. Off course, these two properties can be set while calling the parameterized constructor as well.
The purpose of this class is to a) store the reference to the parent object that will subscribe to Undo, b) to maintain the stack of changes for the undo operation and c) to undo the changes made in the parent object.
While we are storing the undo stack, we also can store the Redo stack to reapply the changes that were undone. Therefore our class structure may look like following
The constructor is called with the reference to the parent object that wishes for undo stack to be maintained. All undo, redo operations are performed on this object reference passed.
public ModificationHistory(object parent)
redoStack = new Stack();
undoStack = new Stack();
objParent = parent;
There are two
readonly Boolean properties called
CanRedo that return
false depending on whether the
Redo stacks contain data or not – respectively.
public bool CanUndo
Store method is called from the parent object prior to its property being changed with the property name and value.
public void Store (string propName, object propVal)
PropertyValue pVal = new PropertyValue(propName, propVal);
For the undo operation, we would use reflection on the
objParent to determine the property corresponding to the name stored and then assign it with the old value stored. Since we would be applying the undo, we would then be required to remove the last saved entry from the stack.
public void Undo()
PropertyValue pVal = (PropertyValue)undoStack.Pop();
Type compType = objParent.GetType();
PropertyInfo pinfo = compType.GetProperty(pVal.PropertyName);
object oldVal = pinfo.GetValue(objParent,null);
PropertyValue pRedo = new PropertyValue(pVal.PropertyName, oldVal);
In the above code fragment, what is being done is that first a value if being popped off the undo stack. Since we are storing only
PropertyValue type of objects, the extracted object reference is typed cast as such. Recall that the
objParent is the reference to the parent object whose state was saved. We need use reflection to get a handle on the property concerned. This is achieved by using the Type information of the parent to locate the
PropertyInfo of the property whose name corresponds to
PropertyValue->PropertyName. Once we have this handle, we can invoke the
SetValue method to assign a new value to the object. Since this new value is actually the old value, we effectively achieve the undo mechanism. However, just before we call the
pinfo.SetValue, we need to push the current state of the property into the redo stack.
The subscribing class
To complete the picture we also need to look at the implementation. Lets say that we create an Entity class with these capabilities and then all implementing classes can inherit from this entity class. Let’s make the class diagram as follows
Entity class, we maintain an instance variable of the
ModificationHistory class and then delegate the actions to store, undo and redo operations to this object. The
AddHistory method defined in the Entity class has been declared with a protected scope making this method visible to all classes that inherit from
Entity. In the
User class, if we need to maintain the undo information of
LastName then the code that needs to be written is as follows –
public string LastName
mLastName = value;
A potential problem
As can be seen from the code fragment above, the original value is being pushed to stack. When the Undo is called, then the same property is executed with the original result. This creates a loop. For example- Let say the original value of the
mLastName is “Peter”. Now If this is changed to “Andre” and then to “George”. Therefore in the stack, the values are “Andre” -> “Peter”. Now if we need to undo, what happens? The value of “Andre” gets popped out of stack and then the
User.LastName property is called with this value. Now, at this point in time, the current value of
mLastName is “George” so this will get pushed to the stack. That results in a situation with the stack becoming “George” -> “Peter”. Calling undo will again make it “Andre” -> “Peter”.
So how do we solve this?
This is solved by maintaining a flag to denote whether or not undo operation is being performed. If the flag is set then we should not store the value in the stack. Consider a code fragment in the
protected void AddHistory(string propertyName, object Value)
So the next problem is to identify when to set or reset this
mBeingUndone flag. The best place for this to happen is when the call is made for Undo in the
public void Undo()
mBeingUndone = true;
mBeingUndone = false;
The code presented here maintains two stack for Undo and Redo capabilities. As the object’s state is undone, the content of these stacks may vary. Since the code presented here does not deal with object persistence, it is left to the developer. Also the developer needs to determine whether or not the stacks need to be emptied after persisting the object to database. Some folks may prefer the entire undo stack be applied at once rather than calling
Undo() multiple times. I feel that these choices are best left to the developer as there cannot be a one solution for all problems.