Click here to Skip to main content
Click here to Skip to main content

Generic Memento Pattern for Undo-Redo in C#

By , 16 Mar 2007
 

Introduction

A useful application normally has a conventional feature called "undo" or "undo-redo". It is extremely handy and I can't imagine a cyber world without undo-redo. With that said, it is not easy to implement undo-redo features. Convenience for users and headache for developers usually refer to the same thing. To my knowledge, there are no .NET libraries that have enough capability to handle all undo-redo cases because this feature is very specific to every single application. The purpose of this article is to generalize undo-redo as much as possible and to suggest implementation methodology.

Background

There is a well known design pattern called Memento. It is a common way of storing and restoring application states. I am not going to talk about that in detail. For more information on Memento pattern or application states, start reading from this article on Wikipedia.

The Memento pattern specifies a state. Translating to C# in the traditional way, we get an interface with one property.

interface IMemento
{
   object State
   {
       get;
   }
}  

The above interface is very generic, but it is too generic to contain essential information. It is pretty much just an object, and doesn't give any hints on what to do with it.

An interface that describes behaviors is usually more useful than those describing properties, because they are more flexible and extensible. So my solution to this is redefining the word Memento as "an object that is capable of restoring a target object to one of its previous states". Let's visualize this statement by translating it to C#.

interface IMemento<T>
{
    /// <summary>
    /// Restores target to the state memorized by this memento.
    /// </summary>
    /// <returns>
    /// A memento of the state before restoring
    /// </returns>
    IMemento<T> Restore(T target);
}

Note that the Restore() method returns an IMemento<T> instance; this is to facilitate redo. The return type can be changed to void if redo doesn't have to be supported.

This simple interface (with a single method) may seem useless to you at first glance. Now let's look into how it can be used to facilitate undo and redo. The following are some code snippets from class UndoRedoHistory<T>.

The Undo() method takes the Memento from the top of undo stack and calls Restore. The returned Memento is then pushed onto the redo stack.

/// <summary>
/// Restores the subject to the previous state on the undo stack,
/// and stores the state before undoing to redo stack.
/// </summary>
public void Undo()
{
    inUndoRedo = true;
    IMemento<T> top = undoStack.Pop();
    redoStack.Push(top.Restore(subject));
    inUndoRedo = false;
} 

The Redo() method is just the opposite of the Undo() method.

/// <summary>
/// Restores the subject to the next state on the redo stack,
/// and stores the state before redoing to undo stack.
/// </summary>
public void Redo()
{
    inUndoRedo = true;
    IMemento<T> top = redoStack.Pop();
    undoStack.Push(top.Restore(subject));
    inUndoRedo = false;
} 

The Do() method registers a Memento so that it can be undone. At the same time, the redo stack is cleared.

 /// <summary>
/// Registers a memento with the history, which can be undone.
/// </summary>
public void Do(IMemento<T> m)
{
    if (inUndoRedo)
        throw new InvalidOperationException(
            "Involking do within an undo/redo action.");
    redoStack.Clear();
    undoStack.Push(m);
} 

Using the code

Now that the undo-redo history is in place, the only thing left is to write concrete implementations of IMemento<T>, which is quite application-dependent.

There are two demo projects written to demonstrate the power of this pattern. They have the same functions and user interfaces. Their difference lies in how undo and redo are implemented. I named them "action based undo-redo" and "serialization based undo-redo" respectively.

Action Based Undo-Redo

Every change made to the tracked object is stored in one memento. Mementos are capable of restoring states by carrying out the inverse action.

history.Do(new AddShapeMemento()); // action: add shape
shapepool.Add(GetShape(e));

history.BeginCompoundDo(); //starts a compound memento
foreach (Shape s in toBeRemoved)
{
    int index = shapepool.IndexOf(s);
    history.Do(new RemoveShapeMemento(index, s)); //action: remove shape
    shapepool.RemoveAt(index);
}
history.EndCompoundDo(); //ends a compound memento

The mechanism is very efficient in memory usage. However, the mementos within the undo-redo history are inter-dependent. If one is corrupted, the entire history gets corrupted. What's worse, it is very tedious to implement, as each single action corresponds to its own Memento subtype.

Serialization Based Undo-Redo

The tracking system doesn't care about what changes are made to the tracked object. Whenever there is a change, it stores the complete state of the object by serializing it.

history.Do(new ShapePoolMemento(shapepool)); //store entire shape pool
shapepool.Add(GetShape(e));

history.Do(new ShapePoolMemento(shapepool)); //store entire shape pool
foreach (Shape s in toBeRemoved)
{
    int index = shapepool.IndexOf(s);
    shapepool.RemoveAt(index);
}

As opposed to action based undo-redo, this method is very easy to implement and the mementos are independent of each other. But it has a fatal pitfall - huge memory consumption, if the tracked object is large. As a result, poor time performance will be observed.

Hybrid Undo-Redo (Conceptual)

As you see in the previous examples, the IMemento<T> interface is really flexible to extend. It is also possible, though I didn't implement a demo project, to create a hybrid undo-redo mechanism that combines the power of the two mechanisms mentioned above. The memory consumption and implementation simplicity should be balanced before making design decisions.

Although I'm trying to list as many implementation methods as possible, I won't be surprised if someone comes up and tells me that there is a totally new and much better way of implementing the IMemento<T> interface. So if you do have any good ideas on this, tell me or just leave a note in the comment area at the bottom.

Points of Interest

Compound Memento

You might have already noticed the two strange methods BeginCompoundDo() and EndCompoundDo() in the previous section. They are used to create an internal CompoundMemento<T>, so that all invocations of Do() method occuring between them are grouped together and treated as a single action.

A CompoundMemento<T> is an IMemento<T> with a wrapper Restore(). See code snippet below.

public class CompoundMemento<T> : IMemento<T>
{
    // ...
    private List<imemento /><t>> mementos;
    // ...
    public IMemento<T> Restore(T target)
    {
        CompoundMemento<T> inverse = new CompoundMemento<T>();
        //starts from the last action
        for (int i = mementos.Count - 1; i >= 0; i--)
            inverse.Add(mementos[i].Restore(target));
        return inverse;
    }
}</imemento />

Controlling Capacity

The size of the undo and redo stacks are usually constrained. Unfortunately System.Collections.Generics.Stack<T> doesn't allow us to control it's capacity. So a generic class named RoundStack<T> was implemented for this purpose. It is round as the name suggests. The bottom items of the stack are trimmed off automatically when the capacity exceeds the predefined limit. Space and time overhead is almost zero. Note that no thread safety is ensured, which may be updated later.

History

  • 15 March 2007 - First Draft

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Sean Yixiang Lu
Software Developer
Singapore Singapore
Member
This guy loves computer programming, software design and development. He is interested and specialized in C family languages, especially C#, Java, Objective-C and D Programming Language. Ruby and Python are starting to interest him as well.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberPankaj Nikam10 Apr '13 - 19:48 
5 Awesome article Smile | :)
GeneralUser drawn points with mouse for undo and redomemberMrKyaw20 Feb '12 - 15:08 
Dear Lu Yixiang,
 
Great contribution in here and I would like to check with you this Memento design patterns can use the application I want. My purpose is to draw multiple lines between user drawn points with mouse pointer then I wish to redo and undo depends on the last object of the line segments. Same as your many shapes in your code and the main difference in mine is my line figures are not fix pattern and will change the length according to the user points.
 
I am looking forward to hearing from you good news soon since I was stuck on it a bit long.
 

Thanks and best regards
NewsImplement Undo in c# vb netmemberMember 381005018 Dec '10 - 5:44 
Here is component which gives you Undo and Redo actions in C# and VB Net.
look here:
http://www.undomadeeasy.com/
GeneralMy vote of 5memberBigdeak24 Aug '10 - 21:10 
Very good article! I'm working on a tile set leveleditor outside of my job.
Yesterday i implemented the memento pattern for undo/redo the actions, this article was a great help for this, thank you! Smile | :)
GeneralExcellent Contribution!memberbl_miles3 Jun '10 - 14:40 
Thanks for submitting/posting this code. You have saved me DAYS of work!
 
I do have a couple questions IF you're still monitoring this or if anyone else wants to chip in...
 
What if the user was able to change a property (say colour) of an object already on the drawing?
How could we implement undo/redo on THAT?
 
Anyways, VERY IMPRESSED with this!
 
Thanks
GeneralThanks for shareing this code...memberrejectkosta19 Apr '10 - 4:48 
Hi, i have problem to implement this engine...
I have one class Person(person have first name, last name and color) and one form. On form I have 3 buttons, they create 3 different persons, and their data are displayed in 9 text boxes(3 per person, for first name, last name and color). Can someone help me and write code for this scenario?
GeneralPretty sure this is the best C# VS2005 compatible Undo/Redo example out there.memberjimmyw40428 Oct '09 - 5:17 
Good job, Lu.
GeneralMemory LeaksmemberMember 216081714 Jan '09 - 6:01 
Lu, Very nice article.
Did any one notice an increase in memory usage while testing this undo-redo implementation. I have noticed some. And it does not go away even by forcing GC. I am looking into it. Let me know if you have seen it too, and any possible pointers to fix this.
GeneralRe: Memory LeaksmemberLu Yixiang14 Jan '09 - 15:06 
I don't think there are leaks. Calling the GC() method doesn't force the garbage collector to actually run the collection.
try minimize your window and restore it again.
 


Never trust a computer you can't throw out a window.

GeneralRe: Memory LeaksmemberMember 216081720 Jan '09 - 9:11 
I did some more tests. I agree, there are no memory leaks.
Thanks
GeneralTreeView nodesmemberGavrilovici Corneliu14 Jul '08 - 5:29 
Hi, I'm a newbie. I tried your code but I cannot figure it out how to use it for the Nodes of a tree (ex. undo deleted nodes).
 
Can you give me a short code example?
 
Thanks
QuestionNewbie questionmemberismirwurscht29 May '07 - 5:10 
Isn't it a disadvantage if the UndoRedoHistory refers to a certain type? In your example it is bound to the ShapePool since it is the only thing that changes. What if something different exists that should be (un-/re-)done?
 
Consider a (not so realistic) text that is held in a String beside the ShapePool. Modifications to the shapes and to the text (that is displayed in a TextBox at the bottom of the form) would lead to states that are recorded by the history. But the changes should be recorded chronologically so the undo history could be:
1. change text
2. add shape
3. change text
4. change text
5. remove shape
 
How would you do this? What would be the type that you use for the UndoRedoHistory?
 
Cheers,
Robert
 


AnswerRe: Newbie question [modified]memberLu Yixiang29 May '07 - 7:24 
The generic T here refers to the type of the target object whose state is to be restored. If text changes occurs to a shapepool, the change is reflected by the corresponding memento, say TextChagnedMemento<ShapePool>. The change information is held within the class, and the type in <> mains ShapePool because it is ShapePool (or its sub-elements) whose states changes.
 
The TextChangedMemento should be something like this:
 

public class TextChangedMemento<ShapePool> : IMemento<ShapePool>
{
private string oldText;
public TextChangedMemento(string oldText)
{
this.oldText = oldText;
}
...
...
}


 
Never trust a computer you can't throw out a window.

GeneralRe: Newbie questionmemberismirwurscht29 May '07 - 19:27 
> If text changes occurs to a shapepool
 
But what if the text exists beside the ShapePool, not in the ShapePool? So the text is independent of the shapes. However, the UndoRedoHistory should reflect the changes that are done variously in the ShapePool and the TextBox chronologically correct. Is this possible with your solution? A TextChangedMemento wouldn't be useful, right?
 
Consider that the ShapePool is just a small part of the business model, so don't regard the text as an exception.
GeneralRe: Newbie questionmemberLu Yixiang29 May '07 - 20:17 
In this case, there should be something bigger than ShapePool, to be put within <>.
There are maybe cases that information to be tracked is loosely scattered the program, like your example of TextBox. In this case, undo redo will definitely be messy. A better practice is to put all the information you care within well design structures, one of which is a root level class. And this root will be used as the target object type.
 
Never trust a computer you can't throw out a window.

AnswerRe: Newbie questionmemberismirwurscht29 May '07 - 23:47 
Thank you for your thoughts!
GeneralRe: Newbie questionmemberneyerMat11 May '12 - 5:59 
Hi,
 
I don't know if you still follow this article. Anyway...
 
At first I want to thank you for sharing this. It's really a great article.
 
But there are still some questions:
 
In general I would prefer the action based history, at least till now Wink | ;-)
 
Complex Types
 
Please imagine a more complex class design as mentioned before in this thread.
I think a good example would be CAD-data (or any other structerd document):
 
-Document
|--Group (List)
|--Part (List)
 
the Type for IMemento should be Document? right?
 
you would have mementos for add/remove Group and Part.
 
EXAMPLE remove a part --> the "RemovePartMemento : IMemento" would look something like this:
 
//PSEUDO CODE!!
public class RemovePartMemento : IMemento<Document>
{
    private Group _Group;
    private Part _RemovedPart;
    public RemovePartMemento(Part removedPart, Group group)
    {
         this._Group = group;
         this._RemovedPart = removedPart;
    }
    Restore(Document target)
    {
        ...
        group.Add(removedPart);
        ...
    }
}
 
So you can (re)store on which group the part was right in the memento. I think this would be a proper way to handle complex types. Isn't it?
 
General Mementos
 
I wonder how to implement "general" Mementos.
EXAMPLE if you would redo/undo changes to properties of a class with reflection. You would have to Implement PropertyChangedMemento for each Type.
 
here's what I got so far for this case (whitout Type). The target is stored within the memento. That's maybe completely wrong as "UndoRedoHistory" should hold the target. But than you would have to implement the PropertyChangedMemento for every "root"-class AND it would not work on complex Types. Maybe I just don't see the obviously. WTF | :WTF:
 
    
public class PropertyChangedMemento : IMemento 
    {
        public PropertyChangedMemento(PropertyInfo pi, object newValue, object oldValue, object changedObject)
        {
            this.PropertyInfo = pi;
            this.NewValue = newValue;
            this.OldValue = oldValue;
            this.Target = changedObject;
        }
        public IMemento Restore()
        {
            this.PropertyInfo.SetValue(ChangedObject, OldValue, null);
            return new PropertyChangedMemento(PropertyInfo, OldValue, NewValue, ChangedObject);
        }
        public object Target
        {
            get;
            set;
        }
        public PropertyInfo PropertyInfo { get; private set; }
        public object NewValue { get; private set; }
        public object OldValue { get; private set; }
        public object ChangedObject { get; private set; }
    }

GeneralSmall bug reportmemberAnderson C Gaudio25 May '07 - 3:48 
Run the program;
Draw some forms;
Ctrl-Z, Ctrl-Z, Ctrl-Z;
Ctrl-Y, Ctrl-Y, Ctrl-Y;
 
No problem.
 
Run the program;
Draw some forms;
Menu Edit/Undo;
Ctrl-Y;
 
Ctrl-Y does not redo.
 
Anderson
GeneralRe: Small bug reportmemberLu Yixiang29 May '07 - 7:27 
Thank you very much for the report. It's a good remind for the rest of reader that the demo projects are just "demo" project. They are far from perfect and are just examples of showing how this pattern can be used.
 
Never trust a computer you can't throw out a window.

GeneralRe: Small bug reportmemberGeorgeMc5 Nov '12 - 1:48 
I couldn't resist looking for what was wrong here, albeit 5 years too late!
 
The problem is that the keyboard shortcuts are dependent on the corresponding menu items being enabled, and the correct setting for menu items is exclusively made in the callback for dropping down the menu. So when you hit a keyboard shortcut, it may or may not be enabled as you expect.
 
Initially the undo/redo menu items should not be enabled - then set their Enabled status where the code is currently invalidating the form, rather than in a menu dropdown callback.
 
Nice article!
GeneralVS 2003 .Net 1.1 SamplememberBuddhi Dananjaya3 May '07 - 1:55 
Hi all,
Can anyone or the author can pass me same implemantation in VS 2003(.Net 1.1) I tried but faced some problems with this .. <T> How to implement this on VS 2003
 
Appriciate early reply....
 
- Regds
 

 

 

 

 
- Buddhi From Sri Lanka -

AnswerRe: VS 2003 .Net 1.1 SamplememberLu Yixiang3 May '07 - 3:22 
I'm very sorry, but this <T> (which is called generics by the way) is only supported by .NET Framework 2.0 and above.
If the reason you are using VS2003 is you don't have a copy of VS2005, you can download "Visual C# 2005 Express Edition" here:
http://msdn.microsoft.com/vstudio/express/
Too bad if you have to use VS2003 for the time being, try upgrade later.

 

 
VALUE LIFE; STAY AWAY FROM CODING.

GeneralSimple and ElegantmemberPman752 May '07 - 5:58 
Very simple and elegant solution you have demonstrated. Nicely done.
 
That's why I enjoy coding, simple/elegant solutions are what makes it all worthwhile.
 
If only I could of found similar simple/elagant solutions when I was doing my mathematic degree lol!
Generalundo-redo for listviewmembermm3107 Apr '07 - 11:25 
I need to implement an undo and redo for an entire row in a listview .

AnswerRe: undo-redo for listviewmemberLu Yixiang14 Apr '07 - 6:46 
You could use the action-base technique discussed in the article.
I've implemented and undo-redo for an entire variable size table in another project, so list shouldn't be a big problem.

 
VALUE LIFE; STAY AWAY FROM CODING.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 16 Mar 2007
Article Copyright 2007 by Sean Yixiang Lu
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid