Undo and Redo
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
Selectionobject 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
| Member | Does |
|---|---|
| Undo | Undoes the last action |
| Redo | Restores the last undone action |
| CanUndo, CanRedo | Whether there is an action to undo or redo |
UndoActionName, RedoActionName | The 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 whatUndoActionNamereturns afterward;nullor an empty string are allowed when no name is needed.Selection.Textinserts the first line, where "\r\n" ends the paragraph.- The prepared
Selectionobject inserts the bold name. EndUndoActionin thefinallyblock 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
- Search and Replace: replace text in one undo step.
- Clipboard Operations: copy, cut and paste.
- Keyboard Shortcuts: all keys of the editor.
- Track Changes: record changes for review instead of undoing them.