TX Text Control .NET for Windows Forms

Undo and Redo

Topic

TextControl keeps an undo buffer of the changes made to a document, whether a user typed them or your code made them. Users step back and forward through it with Ctrl+Z and Ctrl+Y. Your code can offer the same commands in its own menus, show the name of the action that will be undone, group several changes into one step and switch the buffer off where it only costs time and memory.

The examples extend the form of Create Your First Windows Forms Application, with the editor textControl1.

How the Undo Buffer Works

  • Keys. Ctrl+Z undoes the last action, and Ctrl+Y restores it. Both work without code.
  • Code counts, too. Changes your code makes through the API are recorded, except changes made with the properties and methods of a header or footer, which HeaderFooter.AddUndoUnit adds to the buffer. Each property set on the current selection is a step of its own, while a prepared Selection object that is assigned once is a single step; see Selections and Caret Position.
  • Names. Every step has a name, which UndoActionName and RedoActionName return. Your menus can show it, as in "Undo Insert Closing" below.

Add Undo and Redo Commands

MemberDoes
UndoUndoes the last action
RedoRestores the last undone action
CanUndo, CanRedoWhether there is an action to undo or redo
UndoActionName, RedoActionNameThe name of that action, or null if there is none

An Edit menu calls Undo and Redo from its items and enables them with CanUndo and CanRedo each time it opens, as Integrating Existing Menus and Toolbars builds it with a MenuStrip. With the action names, the items also say what they undo. The following DropDownOpening handler of that menu, whose items are named undoToolStripMenuItem and redoToolStripMenuItem, adds the name of the action to their text:

private void EditToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
{
    // Show which action the next click undoes or redoes
    undoToolStripMenuItem.Enabled = textControl1.CanUndo;
    undoToolStripMenuItem.Text = textControl1.CanUndo ? "&Undo " + textControl1.UndoActionName : "&Undo";

    redoToolStripMenuItem.Enabled = textControl1.CanRedo;
    redoToolStripMenuItem.Text = textControl1.CanRedo ? "&Redo " + textControl1.RedoActionName : "&Redo";
}

Type a few words, make them bold and open Edit: the Undo item carries the name of the last step. Click it, and the text is regular again.

Group Several Changes Into One Step

A command of your application often makes several changes, such as inserting text and formatting part of it. Without grouping, the user has to undo each of them separately. BeginUndoAction starts a named group, and EndUndoAction closes it; everything in between is undone and redone in one step.

The following method inserts a closing formula at the input position as one step named "Insert Closing". Call it from a button or menu item of your form:

private void InsertClosing()
{
    // Everything until EndUndoAction is undone in one step
    textControl1.BeginUndoAction("Insert Closing");

    try
    {
        textControl1.Selection.Text = "Kind regards,\r\n";

        Selection name = new Selection();
        name.Text = "Tim Typer";
        name.Bold = true;
        textControl1.Selection = name;
    }
    finally
    {
        textControl1.EndUndoAction();
    }
}

The code works as follows:

  • BeginUndoAction("Insert Closing") opens the group. The name is what UndoActionName returns afterward; null or an empty string are allowed when no name is needed.
  • Selection.Text inserts the first line, where "\r\n" ends the paragraph.
  • The prepared Selection object inserts the bold name.
  • EndUndoAction in the finally block closes the group even if a statement in between throws an exception.

After a click, Edit shows Undo Insert Closing, and one click removes both lines. Search and Replace uses the same pattern to undo all replacements at once.

Clear the Buffer After Loading a Document

Load replaces the content of the editor. If the user shouldn't be able to undo their way back to the previous content, clear the buffer with ClearUndo right after loading:

private void LoadTemplate(string path)
{
    textControl1.Load(path, StreamType.WordprocessingML);

    // The user cannot undo the loading of the template
    textControl1.ClearUndo();
}

Switch the Buffer Off

Recording every change costs time and memory. When your code builds a large document in the editor, for example a report, set AllowUndo to false first, and back to true when the user takes over:

textControl1.AllowUndo = false;

try
{
    // Build the document here
}
finally
{
    // Let the user undo their own changes again
    textControl1.AllowUndo = true;
}

The finally block switches the buffer on again even if the code in between throws an exception.

Documents that are created without being shown to a user are built with ServerTextControl instead; see Document Processing.

Next Steps