Не могу разобраться с реализацией функций Undo/Redo, стоит ли мне использовать Stack?

Я сейчас немного запутался, наверное, в один из тех дней.

Мне нужно реализовать функциональность Undo и Redo для формы. Для простоты, скажем, я сохраняю только тот элемент управления, который был изменен, и значение, которое он имел, когда покинул Focus.

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

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

Код нужен, не очень, но поможет. Меня больше интересует алгоритм, который мне нужно реализовать. Какие-либо предложения?

4 ответа

Решение

Да, вы бы использовали стек. Есть несколько способов сделать это; прочитайте эти ссылки:

http://en.wikipedia.org/wiki/Command_pattern

http://en.wikipedia.org/wiki/Memento_pattern

У каждого есть свои плюсы / минусы.

Я бы использовал интерфейс IUndoableAction. Реализации могут хранить любые данные, которые им нужно было сделать и отменить. Тогда да, я бы использовал стек, чтобы держать их.

interface IUndoableAction
{
    void Do();
    void Undo();
}
Stack<IUndoableAction> Actions;

Каждый вид действия будет реализовывать методы Do и Undo.

Тогда где-то были бы эти два метода:

    void PerformAction(IUndoableActionaction)
    {
        Actions.Push(action);
        action.Do();
    }

    void Undo()
    {
        var action = Actions.Pop();
        action.Undo();
    }

Что касается того, что хранить в классах действий, некоторые действия могут просто хранить старое значение. Тем не менее, однажды у меня было действие, чтобы поменять две строки в электронной таблице. Я не сохранял значения каждой ячейки в обеих строках - я просто сохранял индексы строк, чтобы их можно было вернуть обратно. Может быть легко заполнить тонны памяти, если вы сохраняете все это состояние для каждого действия.

Затем вам также нужен стек повторного выполнения, а когда вы отменяете действие, он помещается в стек повторного выполнения. Стек повторного выполнения необходимо очистить при выполнении нового действия, чтобы все не вышло из строя.

Стек идеально подходит, если вы вставляете "изменение" в него, а когда отменяете, вынимаете "изменение" из него. Затем вы помещаете это всплывающее изменение в другой стек, представляющий повтор. В какой-то момент в будущем, надеемся на сохранение, вы очистите оба стека.

На самом деле это не так просто, так как вам нужно записать тип изменения, понять старые и новые значения и т. Д. Поэтому, когда вы извлекаете из стека отмены, всплывающее окно должно описывать то, что было предыдущим значением и каким оно контролируется. был установлен в.

Обратное для стека повторения, оно должно понимать, что было новым значением и куда оно пошло. Но да, идея двух стеков - это хорошее начало для отмены повторений.

Хорошим примером отмены на основе бизнес-объекта является CSLA.NET, который имеет UndoableBase:

http://www.lhotka.net/cslanet/

http://www.koders.com/csharp/fidCF6AB2CF035B830FF6E40AA22C8AE7B135BE1FC0.aspx?s=serializationinfo

Однако это записывает снимок состояния объекта, поэтому он будет более продвинутым, чем ваша концепция на основе форм. Однако CSLA.NET предлагает полную поддержку привязки данных, поэтому объект, связанный с данными, наследуется от UndoableBase будет естественно поддерживать отмену (не повтор) в пользовательском интерфейсе.

Вероятно, самое простое - иметь комбинацию стеков отмены и повторения.

Альтернатива - иметь массив или список действий и просто увеличивать / уменьшать указатель на индекс в массиве. Когда действие отменено, индекс перемещается назад на единицу, а когда действие повторяется, индекс перемещается вперед на единицу. Преимущество здесь состоит в том, что вам не требуется последовательность "нажимай и нажимай" для каждого действия.

Что нужно учитывать:

  • Если вы отменили несколько раз, а затем выполнили какое-либо действие, все действия повтора должны быть отменены.
  • Убедитесь, что вы проверили границы и убедитесь, что есть действие, которое можно отменить / повторить, прежде чем пытаться выполнить отмену / повтор.
Другие вопросы по тегам