Reputation: 1969
I'm working on a very simple Undo feature for a TextBox and I've got a weird problem. When I try to take strings from the Stack
that holds all the changes and put them inside the Textbox I don't see any changes.
I made a little Debug Label to check if this is really working or not. I found out that it is working in the label, but in the Textbox it uses its own Undo functions.
Is there a way to cancel or override the Textbox Undo and use my own function?
Here is sample code from the change I made:
private void Form1_KeyDown(object sender, KeyEventArgs e)
if (e.KeyCode == Keys.Z && (ModifierKeys & Keys.Control) == Keys.Control)
{
nameTextBox.Text = undoName.GetLastChange(); //--> not working
undoDebuglabel.Text = undoName.GetLastChange(); --> working
}
}
The GetLastChange() is getting the info from a Stack
inside the class.
It's like the Textbox is not letting me to see the changes.
Could it be because I'm using the same shortcut, CTRL + Z
to do it?
Upvotes: 5
Views: 9694
Reputation: 856
I expanded on Geregor's and Keyboard's answer. I couldn't get the code to work as posted in Visual Studio 2017, so made a minor tweak. Then I wanted a Redo function as well, so I added a few more lines of code. This seems to work fine in my testing and I thought I would share since it answers the question and expands.
This code adds undo, redo, and you can hold down the Ctrl-Z or Ctrl-Y to have it "run" through the list.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace CodeBuilder
{
public class HistoryTextBox : System.Windows.Forms.TextBox
{
bool ignoreChange = true;
List<string> storageUndo = null;
List<string> storageRedo = null;
protected override void OnCreateControl()
{
base.OnCreateControl();
storageUndo = new List<string>();
storageRedo = new List<string>();
ignoreChange = false;
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
if (!ignoreChange)
{
this.ClearUndo();
if (storageUndo.Count > 2048) storageUndo.RemoveAt(0);
if (storageRedo.Count > 2048) storageRedo.RemoveAt(0);
storageUndo.Add(this.Text);
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Z && (ModifierKeys & Keys.Control) == Keys.Control)
{
this.ClearUndo();
ignoreChange = true;
this.Undo();
ignoreChange = false;
}
else if (e.KeyCode == Keys.Y && (ModifierKeys & Keys.Control) == Keys.Control)
{
this.ClearUndo();
ignoreChange = true;
this.Redo();
ignoreChange = false;
}
else
{
base.OnKeyDown(e);
}
}
public void Redo()
{
if (storageRedo.Count > 0)
{
this.ignoreChange = true;
this.Text = storageRedo[storageRedo.Count - 1];
storageUndo.Add(this.Text);
storageRedo.RemoveAt(storageRedo.Count - 1);
this.ignoreChange = false;
}
}
public new void Undo()
{
if (storageUndo.Count > 0)
{
this.ignoreChange = true;
storageRedo.Add(this.Text);
this.Text = storageUndo[storageUndo.Count - 1];
storageUndo.RemoveAt(storageUndo.Count - 1);
this.ignoreChange = false;
}
}
}
}
Upvotes: 2
Reputation: 69372
Clear the Textbox's own stack by using the ClearUndo method. Try this:
nameTextBox.ClearUndo();
nameTextBox.Text = undoName.GetLastChange();
Upvotes: 4
Reputation: 6805
You can create your own TextBox to handle history by inheriting from System.Windows.Forms.TextBox. Take a look at my sample:
public class HistoryTextBox: System.Windows.Forms.TextBox
{
bool ignoreChange = false;
List<string> storage = null;
protected override void OnCreateControl()
{
base.OnCreateControl();
//init storage...
storage = new List<string>();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
//save change to storage...
if (!ignoreChange)
{
storage.Add(this.Text);
}
}
public void Undo()
{
if (storage.Count > 0)
{
this.ignoreChange = true;
this.Text = storage[storage.Count - 1];
storage.RemoveAt(storage.Count - 1);
this.ignoreChange = false;
}
}
}
Everytime you need to undo just call:
historyTextBox1.Undo();
This class will give you multiple histoy records.
Upvotes: 3