Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何为文本框实现良好且高效的撤消/重做功能_C#_Undo Redo_Memento - Fatal编程技术网

C# 如何为文本框实现良好且高效的撤消/重做功能

C# 如何为文本框实现良好且高效的撤消/重做功能,c#,undo-redo,memento,C#,Undo Redo,Memento,我有一个文本框,我想为它实现撤销/重做功能。我想它可能已经有了一些轻微的撤销功能,但它有缺陷吗?无论如何,我想实现撤销和重做功能,也只是为了了解您将如何继续并做到这一点 我已经阅读了关于CodeProject的文章,并查看了一些关于CodeProject的示例。死亡的模式是有意义的。我就是不知道该如何实现它。以及如何有效地处理文本框的内容 当然,当TextChanges时,我可以只存储textbox.Text,但这会很快占用大量内存,特别是当textbox包含大量文本时 因此,无论如何,我正在寻

我有一个文本框,我想为它实现撤销/重做功能。我想它可能已经有了一些轻微的撤销功能,但它有缺陷吗?无论如何,我想实现撤销和重做功能,也只是为了了解您将如何继续并做到这一点

我已经阅读了关于CodeProject的文章,并查看了一些关于CodeProject的示例。死亡的模式是有意义的。我就是不知道该如何实现它。以及如何有效地处理
文本框的内容

当然,当
TextChanges
时,我可以只存储
textbox.Text
,但这会很快占用大量内存,特别是当
textbox
包含大量文本时


因此,无论如何,我正在寻找一些关于如何实现这个功能的好的、清晰的和有效的方法的建议。一般而言,尤其是对于文本框c“,)

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

事实上,这是一种纪念模式

你看过这些吗?这是一个如何做的问题


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

之后清除该状态。以下是一种用最少代码实现该状态的方法: (这是win表单背后的代码,表单上只有一个文本框)

公共部分类表单1:表单
{
Stack undoStack=新堆栈();
公共表格1()
{
初始化组件();
}
私有void文本框\u KeyDown(对象发送方,KeyEventArgs e)
{
if(e.KeyCode==Keys.U&&Control.ModifierKeys==Keys.Control&&undoStack.Count>0)
undoStack.Pop();
}
私有无效文本框\按键(对象发送器,按键事件参数e)
{            
if(e.KeyChar!=“u”| Control.ModifierKeys!=Keys.Control)
{
var textBox=(textBox)发送方;
撤消堆栈.Push(textBox.Text(textBox.Text));
}
}
}
公共静态类扩展
{
公共静态函数文本(此文本框,字符串文本)
{            
return()=>{textBox.Text=Text;return textBox;};
}
}

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

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

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


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

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

当我开始实现它时,我惊讶于它最终是多么简单和优雅。
这使我赢得了胜利。

最聪明的方法是使用不可变的持久对象。不要对对象进行更改,只创建与旧版本略有更改的新对象。这可以通过只克隆热路径上树的一部分来实现

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

 [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);

}

您可以在这里找到完整的源代码

我也需要在撤消/重做时将选择重置为其原始位置。请看我刚刚编写的基本代码底部的“类扩展”,了解一个只包含一个文本框“textBox1”的表单:

公共部分类表单1:表单
{
Stack undoStack=新堆栈();
Stack redoStack=新堆栈();
公共表格1()
{
初始化组件();
textBox1.KeyDown+=textbox1u KeyDown;
}
私有void TextBox1\u KeyDown(对象发送方,KeyEventArgs e)
{
如果(e.KeyCode==Keys.ControlKey&&ModifierKeys==Keys.Control){}
else if(e.KeyCode==Keys.U&&ModifierKeys==Keys.Control)
{
如果(undoStack.Count>0)
{
StackPush(发送器,redoStack);
undoStack.Pop();
}
}
else if(e.KeyCode==Keys.R&&ModifierKeys==Keys.Control)
{
如果(redoStack.Count>0)
{
StackPush(发送器,撤消堆栈);
redoStack.Pop();
}
}
其他的
{
redoStack.Clear();
StackPush(发送器,撤消堆栈);
}
}
私有void StackPush(对象发送器、堆栈)
{
TextBox TextBox=(TextBox)发送方;
var tBT=textBox.Text(textBox.Text,textBox.SelectionStart);
堆栈推送(tBT);
}
}
公共静态类扩展
{
公共静态函数文本(此文本框、字符串文本、整数选择)
{
return()=>
{
Text=Text;
textBox.SelectionStart=sel;
返回文本框;
};
}
}

那么,你会制作一个文本框的扩展版本吗?实现该接口?需要多少级别的撤消?n个级别还是1个级别?撤销行为是在对象上还是仅在文本框的数据上?为什么要使用函数堆栈而不是字符串堆栈?@Jack读取
 [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);

}
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;
    }
}
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;
        };
    }
}