如何为TextBox实现高效的撤消/重做function

我有一个TextBox,我想为它实现撤消/重做function。 我已经读过它可能已经有一些轻微的撤消function,但它有错误吗? 无论如何,我想实现撤消和重做function,也只是为了了解如何继续这样做。

我已经阅读了有关Memento模式的内容,并在CodeProject上查看了一些Generic Undo / Redo示例。 并且模式kiiind是有道理的。 我似乎无法围绕如何实现它。 以及如何有效地为TextBox的内容做到这一点。

当然,我可以在TextChanges时存储textbox.Text ,但这会非常快地占用相当多的内存,特别是如果TextBox包含大量文本。

所以无论如何,我正在寻找一些关于如何实现一种良好,清晰和有效的方法来实现这一function的建议。 一般而言,特别是对于TextBox c“,)

.NET System.ComponentModel命名空间附带一个IEditableObject接口,您也可以使用INotifyPropertyChangingINotifyPropertyChanged 。 MVC模式还可以使您的界面通过事件响应模型中的更改,从而更新或恢复文本框的值。

有效的纪念模式

你有没看过这些? 这是一个如何。

一个简单快捷的版本是存储文本框OnTextChanged的状态。 每个撤消都将返回Array中的最后一个事件。 C#Stack Type在这里很方便。 一旦离开界面或者在Apply之后,您可以清除状态。

这是用最少的代码实现它的一种方法:(这是一个win表单背后的代码,上面有一个文本框)

 public partial class Form1 : Form { Stack> undoStack = new Stack>(); public Form1() { InitializeComponent(); } private void textBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.U && Control.ModifierKeys == Keys.Control && undoStack.Count > 0) undoStack.Pop()(); } private void textBox_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar != 'u' || Control.ModifierKeys != Keys.Control) { var textBox = (TextBox)sender; undoStack.Push(textBox.Text(textBox.Text)); } } } public static class Extensions { public static Func Text(this TextBox textBox, string text) { return () => { textBox.Text = text; return textBox; }; } } 

通过为其他输入类型实现扩展方法,undoStack可以为整个UI提供服务,按顺序撤消所有UI操作。

这里有一个很好的解决方案:

向应用程序添加撤消/重做或后退/前进function

撤消/重做function文本框(winforms)

代码在VB.NET中,但您可以轻松地将其转换为C#而不需要太多努力。 在线转换器也可用。

这是我在该主题上找到的最有用的页面,更通用,适用于undo / redo堆栈上的不同对象类型。

命令模式

当我实现它时,我很惊讶它最终变得如此简单和优雅。 这对我来说是一场胜利。

最智能的方法是使用不可变的持久对象。 永远不要对对象进行更改,只创建与旧版本稍有不同的新对象。 这可以通过仅在热路径上克隆树的部分来有效地完成。

我有一个用最少的代码编写的撤销堆栈的例子

  [Fact] public void UndoStackSpec() { var stack = new UndoStack(new A(10, null)); stack.Current().B.Should().Be(null); stack.Set(x => xB, new B(20, null)); stack.Current().B.Should().NotBe(null); stack.Current().BPShould().Be(20); stack.Undo(); stack.Current().B.Should().Be(null); } 

其中A和B作为所有属性上具有private setters类,即immutable

 class A : Immutable { public int P { get; private set; } public BB { get; private set; } public A(int p, B b) { P = p; B = b; } } class B : Immutable { public int P { get; private set; } public CC { get; private set; } public B(int p, C c) { P = p; C = c; } } class C : Immutable { public int P { get; private set; } public C(int p) { P = p; } } 

你可以在这里找到完整的源代码https://gist.github.com/bradphelan/5395652

在撤消/重做时,我还需要将选择重置为其原始位置。 在我的基本和良好工作代码的底部观看“类扩展”,只需一个文本框“textBox1”即可尝试:

 public partial class Form1 : Form { Stack> undoStack = new Stack>(); Stack> redoStack = new Stack>(); public Form1() { InitializeComponent(); textBox1.KeyDown += TextBox1_KeyDown; } private void TextBox1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.ControlKey && ModifierKeys == Keys.Control) { } else if (e.KeyCode == Keys.U && ModifierKeys == Keys.Control) { if(undoStack.Count > 0) { StackPush(sender, redoStack); undoStack.Pop()(); } } else if (e.KeyCode == Keys.R && ModifierKeys == Keys.Control) { if(redoStack.Count > 0) { StackPush(sender, undoStack); redoStack.Pop()(); } } else { redoStack.Clear(); StackPush(sender, undoStack); } } private void StackPush(object sender, Stack> stack) { TextBox textBox = (TextBox)sender; var tBT = textBox.Text(textBox.Text, textBox.SelectionStart); stack.Push(tBT); } } public static class Extensions { public static Func Text(this TextBox textBox, string text, int sel) { return () => { textBox.Text = text; textBox.SelectionStart = sel; return textBox; }; } } 

我会监听一个更改事件,当它发生时,将前一个状态和当前状态的diff推到一个堆栈上。 差异应该比存储整个文本小得多。 此外,您可能不希望在每次编辑时将新的撤消状态推送到堆栈上…例如,我将所有打字组合在一起,直到用户更改光标位置为止。