我有一个TextBox,我想为其实现撤消/重做功能。我have read它可能已经有一些轻微的撤销功能,但它有错误吗?无论如何,我想实现撤消和重做功能,也只是为了了解如何继续这样做。
我已阅读有关Memento Pattern的内容,并在CodeProject的Generic Undo/Redo示例中查看了一些内容。而模式kiiind是有道理的。我似乎无法围绕如何实现它。以及如何有效地完成TextBox
的内容。
当然我可以在textbox.Text
时存储TextChanges
,但这会非常快地占用相当多的内存,尤其是TextBox
包含大量文本时。
所以无论如何,我正在寻找一些关于如何实现一种良好,清晰和有效的方法来实现这一功能的建议。一般而言,特别是对于TextBox c“,)
答案 0 :(得分:15)
.NET System.ComponentModel
命名空间附带IEditableObject
界面,您也可以使用INotifyPropertyChanging
和INotifyPropertyChanged
。 MVC Pattern还可以使您的界面通过事件响应模型中的更改,从而更新或恢复文本框的值。
实际上是 Memento模式。
你仔细看过这些吗? Here是如何做到的。简单快捷的版本是存储文本框OnTextChanged
的状态。每个撤消都将返回Array中的最后一个事件。 C#Stack Type在这里很方便。一旦离开界面或Apply
之后,您就可以清除状态。
答案 1 :(得分:6)
这是一种用最少的代码实现它的方法: (这是win表单背后的代码,上面有一个文本框)
public partial class Form1 : Form
{
Stack<Func<object>> undoStack = new Stack<Func<object>>();
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<TextBox> Text(this TextBox textBox, string text)
{
return () => { textBox.Text = text; return textBox; };
}
}
通过为其他输入类型实现扩展方法,undoStack可以为整个UI提供服务,按顺序撤消所有UI操作。
答案 2 :(得分:2)
这里可以找到一个好的解决方案:
Add Undo/Redo or Back/Forward Functionality to your Application
Undo/Redo Capable TextBox (winforms)
代码在VB.NET中,但您可以轻松地将其转换为C#而不需要太多努力。也可以使用在线转换器。
答案 3 :(得分:1)
答案 4 :(得分:1)
我需要在撤消/重做时将选择重置为原始位置。在我的基本和良好工作代码的底部观看“类扩展”,只需一个文本框“textBox1”即可尝试:
public partial class Form1 : Form
{
Stack<Func<object>> undoStack = new Stack<Func<object>>();
Stack<Func<object>> redoStack = new Stack<Func<object>>();
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<Func<object>> stack)
{
TextBox textBox = (TextBox)sender;
var tBT = textBox.Text(textBox.Text, textBox.SelectionStart);
stack.Push(tBT);
}
}
public static class Extensions
{
public static Func<TextBox> Text(this TextBox textBox, string text, int sel)
{
return () =>
{
textBox.Text = text;
textBox.SelectionStart = sel;
return textBox;
};
}
}
答案 5 :(得分:0)
我会监听一个更改事件,当它发生时,将前一个状态的diff
推送到堆栈上。差异应该比存储整个文本小得多。此外,您可能不希望在每次编辑时将新的撤消状态推送到堆栈中...例如,我将所有打字组合在一起,直到用户更改光标位置为止。
答案 6 :(得分:0)
最智能的方法是使用不可变的持久对象。永远不要对对象进行更改,只创建与旧版本稍有不同的新对象。只需在热路径上克隆树的部分,就可以有效地完成这项工作。
我有一个用最少代码编写的撤销堆栈的例子
[Fact]
public void UndoStackSpec()
{
var stack = new UndoStack<A>(new A(10, null));
stack.Current().B.Should().Be(null);
stack.Set(x => x.B, new B(20, null));
stack.Current().B.Should().NotBe(null);
stack.Current().B.P.Should().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 B B { get; private set; }
public A(int p, B b)
{
P = p;
B = b;
}
}
class B : Immutable
{
public int P { get; private set; }
public C C { 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