Подтвердить что ты не робот

Как реализовать хорошие и эффективные функции отмены/повтора для TextBox

У меня есть TextBox, который я бы хотел реализовать для функции отмены/повтора. Я прочитал, что у него могут быть некоторые незначительные функции отмены, но что это глючит? В любом случае, я хотел бы реализовать как функцию отмены и повтора, так и просто узнать, как вы это сделаете.

Я прочитал о Memento Pattern и посмотрел на Общий вариант отмены/повтора в CodeProject. И образец имеет смысл. Я просто не могу представить себе, как его реализовать. И как сделать это эффективно для содержимого TextBox.

Конечно, я мог бы просто хранить textbox.Text, когда TextChanges, но это довольно быстро обернуло бы довольно много памяти, особенно если TextBox содержало много текста.

Так или иначе, я ищу несколько советов о том, как реализовать хороший, понятный и эффективный способ реализации этой функциональности. И вообще, и особенно для TextBox c ",)

4b9b3361

Ответ 1

Пространство имен .NET System.ComponentModel поставляется с интерфейсом IEditableObject, вы также можете использовать INotifyPropertyChanging и INotifyPropertyChanged. MVC Pattern также сделает так, чтобы ваш интерфейс реагировал на изменения в модели посредством событий, таким образом обновляя или восстанавливая значение вашего текстового поля.

Эффектно шаблон заклинания.

Вы изучали их? Здесь - это как.

Простая и быстрая версия - сохранить состояние текстового поля OnTextChanged. Каждое отклонение возвращает последнее событие в массиве. Здесь был бы полезен тип стека С#. Вы можете очистить состояние, как только вы покинете интерфейс, или после Apply.

Ответ 2

Здесь можно достичь минимального кода: (Это код позади формы выигрыша с одним текстовым полем на нем)

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 может обслуживать весь ваш пользовательский интерфейс, отменяя все действия пользовательского интерфейса в порядке.

Ответ 4

Это самая полезная страница, которую я нашел по этой теме, более общий, подходящий для разных типов объектов в стеке отмены/повтора.

Командная строка

Когда я добрался до его реализации, я был удивлен тем, насколько простым и элегантным он оказался. Это делает его победой для меня.

Ответ 5

Самый умный способ - с неизменяемыми постоянными объектами. Никогда не изменяйте объект, только создавайте новые объекты, которые немного меняются от старой версии. Это можно сделать несколько эффективно, только клонируя части дерева на горячем пути.

У меня есть пример стека отмены, написанного с минимальным кодом

 [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

Ответ 6

Мне нужно reset выбрать также его исходные позиции при отмене/повторении. Смотрите "Расширения класса", в нижней части моего простого базового и хорошо работающего кода, для формы с одним текстовым полем "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;
        };
    }
}

Ответ 7

Я бы прослушал событие изменения, и когда это произойдет, нажмите diff предыдущего состояния и текущего состояния на стек. Разница должна быть намного меньше, чем хранение всего текста. Кроме того, вы можете не захотеть перетащить новое состояние отмены в стек при каждом редактировании... Я бы разделил все набрав вместе, пока пользователь не изменит позицию курсора, например.