How to implement Undo feature on TextBox in C# and .NET

How to implement Undo feature on TextBox in C# and .NET

Undo feature on TextBox is the topic of discussion for today’s post.

How to implement Undo feature on TextBox in C# and .NET – Source Code

Undo Feature

You have all used the Undo functionality, and you know, you can’t live without it. It is a simple interaction-based technique implemented in many computer programs. It is almost impossible to build any type of software without considering baking in the Undo feature.

What it does is it erases the last change done to a document, reverting it to an older state. You have probably used this a thousand times. If you are on a Windows, you already know the shortcut. Press Ctrl + Z on your keyboard and it magically reverts you to a previous state.

Well, now it is time to implement this famous Undo feature in your C# application. As an example, I will use the TextBox control. In another tutorial we will see some more advanced strategies to Undo or Redo user actions. To prove my point, I will constantly use different Windows controls that you find yourself working with, in your day-to-day coding.

I hope that this will help you grasp the concepts of Undo and Redo so that you can confidently implement them in your coding routine. However, before we start the exercise, I would like you to look at some classes and C# methods I will be using in this solution. But first, let’s see what we are going to build.

What are we going to build

As you can see, this is a very simple C# application built around the .NET framework. It uses a TextBox control and has an Undo button. As per usual, we are going to be focusing on the task at hand. How can we implement Undo feature on TextBox?

So, whatever we write, copy, insert or delete we would need to be able to undo these operations and restore the previous state of the TextBox control. If you think about it, this is a unique and very interesting problem to solve. It will vary from application to application, from control to control and ultimately it will depend on the client’s requirements.

But this tutorial should help your way of thinking, analyzing and designing the solution to this problem.

If we are ready, let’s dive into coding.

More DevInDeep Tutorials:

C# Common Classes

The first one is Optional class. I use it a lot. The class is inspired from the functional language types like F#, but there is nothing saying you can not use it in OOP language. You will find the full implementation on GitHub.

For out little project this is enough:

public class Optional<T>
{
    private T[] item;
    private Optional() => item = new T[0];
    private Optional(T item) => this.item = new T[] { item };
    public void Do(Action<T> action) => item.Do(action);

    public static Optional<T> Create(T item) => new Optional<T>(item);
    public static Optional<T> Empty() => new Optional<T>();
}

If you copy and paste this code you will see that there are some errors. That is because one extension method is missing. So, here is the C# implementation:

public static class EnumerableExtensions
{
   public static void Do<T>(this IEnumerable<T> enumeration, Action<T> onItem)
   {
      foreach (var item in enumeration)
         onItem(item);
   }
}

This is just a little “trick” extension method to hide away some of the C# language infrastructure. Of course, you can re-do the code without using this extension method as well. But, as a result the class will be more bulky.

There is one more extension method I want to mention now. And it is regarding the Stack<T> class. Since we are implementing Undo feature, naturally we think of the stack collection. It will help us track previous state of out TextBox control. To implement this Undo feature I need a method that the Stack class does not provide. Here it is:

public static class StackExtensions
{
    public static Optional<T> PopThenPeek<T>(this Stack<T> stack)
    {
        if (stack.Count == 1) return Optional<T>.Empty();
        stack.Pop();
        return Optional<T>.Create(stack.Peek());
    }
}

I know this is an interesting choice for a method. But, there are couple of challenges we will have to overcome when working with TextBox. This method will come in quite handy. I will explain it later. For now, you can copy and paste it in your solution. We will discuss it later.

Implement Undo feature on TextBox

Because I want to extend this foundational .NET framework control, I will create a new class and name this new class UndoableTextBox. I think the name is pretty much self explanatory. The important thing here is to remember to inherit from the TextBox control. Here is our C# class:

public class UndoableTextBox : TextBox
{
}

If you build the solution, you will get an UndoableTextBox control in the ToolBox in your Visual Studio.

Undoable TextBox Control

You can drag this control over your form and then add Undo button right next to it. Something very similar like this:

Undo TextBox in C#

And the only code you will need is this one:

public partial class Form1 : Form
{
    public Form1() => InitializeComponent();
    private void btnUndo_Click(object sender, EventArgs e) => txtText.Command.Undo();
}

Now, let’s see what hides behind the only interesting C# statement here.

public class UndoableTextBox : TextBox
{
    private Action textChanged;
    public void EnableUndo() => textChanged = () => Command.Add(new TextChangeCommand(this, Text));
    public void DisableUndo() => textChanged = () => { };
    public CommandController Command { get; private set; } = new CommandController();
    public UndoableTextBox()
    {
        Command.Add(new EmptyStringCommand(this));
        TextChanged += UndoableTextBox_TextChanged;
        EnableUndo();
    }
    private void UndoableTextBox_TextChanged(object sender, EventArgs e) => textChanged?.Invoke();
}

This may come out as a lot, but it is quite simple actually. Let’s start at the constructor level. The first statement adds an instance of EmptyStringCommand to the Command property. This property actually holds our stack of commands executed for this TextBox control. Since the control starts out by being empty, I am simply adding this state to the stack.

In order to follow the text changes, I will subscribe to the TextChanged event of the TextBox control. Now, you can see here that I am invoking a textChanged delegate. Like I said in the beginning, implementing Undo feature on TextBox control can be tricky (just a little). Think about the following scenario:

A problem

A user types some text in the text field. Then he decides to undo the action. So, what happens in the background? Well, as you can from the code, we wrote earlier, the text changed event will be raised as user is typing. But think again. The same event will be raised when we restore the previous state as well, because we will be changing the text property. If we proceed and change the Text property, then we will have another state in the stack. This state is not something we want, so we want to prevent it. All in all, we do not want to add state we just undid. Does that make sense? You can play with the code and see what I am talking about.

Anyways, how do we sort this out? Well, we can decide when to enable adding a state in the stack explicitly. What I mean by that is, the user will be able to type in freely in the text box and any state generated by him will be stored on the stack. However, if the state is restored from the stack itself it will not go back in. So, the C# methods:

public void EnableUndo() => textChanged = () => Command.Add(new TextChangeCommand(this, Text));
public void DisableUndo() => textChanged = () => { };

EnableUndo and DisableUndo enables us to control what goes into the stack. Later on, in our coding session you will see where these methods will come in handy.

Now, let’s peek behind the CommandController.

The CommandController class

This class is pretty simple. It just delegates the calls to a stack collection which we are going to present in just a moment.

public class CommandController
{
    private readonly Commands commands = new Commands();
    internal void Add(ICommand command) => commands.Push(command);
    internal void Undo() => commands.Pop().Do(command => command.Execute());
}

As you can see from the C# example above, we are creating a new Commands class instance. When we add a command we push it at the top of the stack. When we execute the Undo operation we just pop the latest command on the stack and execute it.

The Stack

Almost every single example implementing Undo feature uses stack as a collection. Because of the nature of this collection as LIFO (last-in-first-out) list, it fits perfectly for solving this types of problems. So, without further ado, here’s the C# code for it:

internal class Commands
{
    private readonly Stack<ICommand> commands = new Stack<ICommand>();
    public void Push(ICommand command) => commands.Push(command);
    public Optional<ICommand> Pop() => commands.PopThenPeek();
}

From the code snippet above, you can see we are using a generic stack collection. I have abstracted away the Stack in a Commands class just in case I wanted to make more changes later. But more so because of one important change to the workings of the stack. But more on that later.

The TextBox Undo feature is a bit different than the others I have implemented, so I needed to do a good amount of thinking to find the best possible solution. The best is the simplest. So here it is.

A LIFO list collection comes as a natural way of solving this problem. But, you can do it your own way. The stack holds a list of commands we have executed so far. Please note, that the last command pushed to the stack is the latest one we have executed. So, as a result when we are executing Undo we need the most recent one, then the next one and so on.

Now, here’s the thing about the implementation. When we write something in our TextBox like:

Hello

When we execute Undo on the TextBox the latest state that we are going to pop is the following one:

Hello

Well, think about it for a second. This is because the state is inserted once the text is changed in the TextBox. And when we insert the “o” character, that is our last state. So, what do we do about it? Well, I coded a special extension method to the stack so that it helps me remove the last state and restore the previous one. In other words, if we execute Undo we should get:

Hell

Now, let’s see the code:

public static class StackExtensions
{
    public static Optional<T> PopThenPeek<T>(this Stack<T> stack)
    {
        if (stack.Count == 1) return Optional<T>.Empty();
        stack.Pop();
        return Optional<T>.Create(stack.Peek());
    }
}

From this method, you can see that I am always keeping my initial state (that is when the textbox is empty). Otherwise I Pop the latest state (remove it from the stack) and Peek into the next one. This allows me to remove the current state we are already in and restore the correct one.

Undo Feature on TextBox Explained

Hello
Hell
Hel
He
H
How the string “Hello” is represented in the stack

Here is what is happening to our stack collection. When the user enters the string “Hello” to our text box. As you can see the stack is filled with all the previous states. The current being “Hello”. Now, you can visualize the problem I was describing. If I only do a Pop on the stack, we would get the word “Hello” which is our current state. But, we don’t want that, we need “Hell”. To get to that state, I execute Pop which will remove the “Hello” string and Peek at the next one in line: “Hell”. This is the one I am returning to the user.

Hell
Hel
He
H
This is how the stack looks like after executing Undo

Undo Feature on TextBox: Commands

When, implementing Undo feature (especially a generic one), we need to introduce the concept of a command. It is a single action executed by the user. We represent that action by an interface like this:

public interface ICommand
{
    void Execute();
    Optional<ICommand> Undo();
}

This is a quite generic interface that will lend itself nicely in our other tutorials on Undo/Redo functionality in the future.

EmptyStringCommand Implementation

internal class EmptyStringCommand : ICommand
{
    private readonly UndoableTextBox undoableTextBox;
    public EmptyStringCommand(UndoableTextBox undoableTextBox) => this.undoableTextBox = undoableTextBox;

    public void Execute()
    {
        undoableTextBox.DisableUndo();
        undoableTextBox.Text = string.Empty;
        undoableTextBox.EnableUndo();
    }

    public Optional<ICommand> Undo() => Optional<ICommand>.Empty();
}

This is the first command we are going to explain, since it is the easiest. The type says it all. This is an EmptyStringCommand which I am using to initialize the Undo stack collection. It consists of UndoableTextBox control, passed through the constructor.

As you can see the Execute method is a bit peculiar.

undoableTextBox.DisableUndo();
//Statements
undoableTextBox.EnableUndo();

First we call the DisableUndo method. Then we execute a statement (in this case set the text box control Text property to empty) and then we call EnableUndo method. This is because setting the Text property of the control will raise the TextChanged event on the TextBox, resulting with this state being at the top of the stack. This is a side effect that we do not want. So to manage that situation I disable the undo functionality, set the textbox to the desired state and enable the undo functionality afterwards.

If we try to call Undo method we will return an empty Optional type, which will not be executed. And it makes sense. There is nothing to Undo at that point.

TextChangeCommand Implementation

public class TextChangeCommand : ICommand
{
    private readonly UndoableTextBox textBox;
    private readonly string text;
    public TextChangeCommand(UndoableTextBox textBox, string text)
    {
        this.textBox = textBox;
        this.text = text;
    }

    public void Execute()
    {
        textBox.DisableUndo();
        textBox.Text = text;
        textBox.EnableUndo();
    }

    public Optional<ICommand> Undo() => Optional<ICommand>.Create(new TextChangeCommand(textBox, text));
}

This implementation of the ICommand interface has a very similar implementation. Except, it does keep track of the string the user enters. Execute is pretty much the same except we do not set the text property to an empty string, we set it to the text entered by the user.

Conclusion

This is just an introduction into implementing Undo/Redo functionality into your code. We will see more of it later. Right now it is important to understand the fundamentals and how to stat designing a robust solution. A lot better example would be an application having multiple undoable controls and create one central Undo/Redo feature which will be able to manage everything that is goin on in the application. So, we will build one more undoable control and place those two side by side. See how we can manage and implement full Undo feature on Windows Controls.