Introduction
Often we come across situations where we need to undo some action done earlier. For example, you add many tree items to a tree view and then you realize you need to undo that action. In other words, you want to delete the items from the tree view. An Undo/Redo feature comes in handy. This feature could be implemented using many techniques. One useful technique is presented at http://www.dofactory.com/Patterns/PatternCommand.aspx. The feature illustrated here is based on this technique, namely the Command design pattern. For more description of this pattern, please visit the link given. However, the feature I have illustrated below adds much more to this. I have added GUI aspects (enabling menu items based on the current state of where Undo/Redo stack stands), which are not mentioned on that page. I have also added state-based behavior to the pattern.
Basics are here:
The classes participating in the design of the feature are:
Command: An abstract class that represents a basic user action such as
adding items to a collection. Contains new (after user performs action)
and old (before user performed action) object states.
ObjectState: Represents the state of the objects involved in Undo/Redo.
CalculatorCommand: Represents a user action.
UndoRedoManager: Manages the Undo/Redo engine by maintaining the Undo/Redo
stack of Commands. It also maintains the current Undo and Redo stack pointers.
The Design
When a user adds a calculation by entering integer values in the operands text boxes, this is recognized as a command and a new object of type CalculatorCommand is created as follows.
Command command = new CalculatorCommand();
The object states for this command are set appropriately since these are required when we undo or redo this command. The command is added to the stack as below.
UndoRedoManager.GetInstance().AddCommandToUndoRedoStack(command);
When user selects Undo from the Edit menu, following code is executed:
UndoRedoManager.GetInstance().ExecuteUndo();
In UndoRedoManager.ExecuteUndo(), the UndoRedoManager simply gets the command corresponding to the current undo stack pointer and calls UnExecute() on that command.
public bool ExecuteUndo()
{
if (m_nCurrentUSP < 0)
{
return false;
}
Command entry = GetCommandWithLevelID(m_nCurrentUSP);
if (entry != null)
{
entry.UnExecute();
}
return true;
}
The same is respectively done when user selects Redo. This is the beauty of the pattern. But you must properly set the object states so that the undo and redo makes the application fall down to proper states.
In Command.UnExecute() (Undo), the old object state is restored and in Command.Execute() (Redo), the new object state is restored.
You can unzip the sources and build the application in VS .Net 2003. Run the application and enter the operands, select the operator and click the "=" button to see the results. Do as many calculations as you wish. Simply undo/redo to see the calculations done earlier/later.
Something about the ObjectState class:
This class can be used to store the object state. It can also store GUI states, such as state of some buttons when the action was performed. You can use this class to store anything related to making the application fall down to proper states. This class is not confined for GUI aspects only.
Future:
Here a GUI example is shown only to illustrate. You can use the idea of this feature to virtually undo/redo anything that could be associated with finite object states. Fuzzy object states cannot be used in this basic implementation. Please feel free to customize this feature.