33

我有一个文本框,我想为其实现撤消/重做功能。我读过它可能已经有一些轻微的撤消功能,但是它有问题吗?无论如何,我想同时实现撤消和重做功能,只是为了了解您将如何继续执行此操作。

我已经阅读了有关Memento 模式的内容,并在 CodeProject上的通用撤消/重做示例中查看了一些内容。而且这种模式很有意义。我似乎无法理解如何实现它。以及如何有效地处理TextBox.

当然,我可以只存储textbox.Textwhen TextChanges,但这会很快占用大量内存,尤其是在TextBox包含大量文本的情况下。

所以无论如何,我正在寻找一些关于如何实现一个好的、清晰和有效的方式来实现这个功能的建议。一般而言,尤其是对于 TextBox c",)

4

7 回答 7

19

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

实际上是纪念品模式

你看过这些吗? 是一个方法。

一个简单快捷的版本是存储文本框的状态OnTextChanged。每个撤消都将返回数组中的最后一个事件。C# 堆栈类型在这里会很方便。您也可以在离开界面后或之后清除状态Apply

于 2009-02-28T09:41:20.497 回答
9

这是一种用最少的代码实现它的方法:(这是带有单个文本框的 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 操作。

于 2010-09-14T02:57:18.287 回答
4

撤消/重做时,我也需要将选择重置为其原始位置。在我的基本且运行良好的代码的底部观看“类扩展”,以尝试只有一个文本框“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;
        };
    }
}
于 2016-08-24T21:09:02.907 回答
3

可以在这里找到一个好的解决方案:

为您的应用程序添加撤消/重做或后退/前进功能

撤消/重做功能文本框(winforms)

代码在 VB.NET 中,但您可以轻松地将其转换为 C#,而无需付出太多努力。还提供在线转换器。

于 2012-04-20T11:56:42.870 回答
3

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

命令模式

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

于 2012-11-13T07:22:29.463 回答
1

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

于 2010-09-14T03:39:51.247 回答
1

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

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

 [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

于 2013-04-16T13:19:06.163 回答