Отменить внутри WPF M-V-VM, как он подходит?

В моих предыдущих проектах я уже реализовал систему отмены в С++, и я знаю, как она работает. Я также знаю шаблон Command.

Я буду использовать настольное приложение С#/WPF и хотел бы основать мой дизайн на шаблоне M-V-VM.

Приложение будет:

  • быть относительно небольшим проектом (рассчитанная на 2-3 недели работа для одного разработчика)
  • имеют простую модель данных с персистентностью (linq to XML)
  • поддержка отменить/повторить

Мне было интересно, есть ли у кого-нибудь опыт внедрения системы отмены, следуя шаблону M-V-VM. Как бы это было в нем? Как это можно извлечь из уведомлений INotifyPropertyChanged и INotifyCollectionChanged, поэтому при реализации моделей (бизнес-объектов) требуется минимальная работа.

Я бы подумал, что система отмены будет каким-то образом интегрирована в уровень ViewModel, так как это состояние пользовательского интерфейса.

Любая мысль?

Ответ 1

Вот решение, которое я использовал для своего проекта. Решение оказалось безупречным.

Система использует объекты отмены событий, где каждое событие отмены известно, как отменить и повторить сам.

interface IUndoEvent
{
    void Undo();
    void Redo();
}

Мне удалось создать систему, выполнив только 2 события отмены: один для изменений свойств; один для изменений коллекции.

Идея состоит в том, что эти события реализуют Undo/Redo путем изменения модели напрямую.

class PropertyChangeUndoEvent : IUndoEvent
{
    private ModelBase _target;
    private string _propertyName;
    private object _oldValue;
    private object _newValue;

    public PropertyChangeUndoEvent(ModelBase target, string propertyName, object oldValue, object newValue)
    {
        _target = target;
        _propertyName = propertyName;
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public void Undo()
    {
        SetValue(_oldValue);
    }

    public void Redo()
    {
        SetValue(_newValue);
    }

    private void SetValue(object value)
    {
        // Set Value on the _target using reflection (_propertyName)
    }
}

ViewModel заботится о создании событий отмены, вызывая функции ViewModelBase:

class MyViewModel : ViewModelBase
{
    public string Name
    {
        get { return _model.Name; }

        // The SetValue will create a undo event, and push it to the UndoManager
        set { SetValue(_model, "Name", value); }
    }
}

Наконец, есть UndoManager (проект singleton), который хранит стопку отмены и стек повтора.

Ответ 2

Вы можете обнаружить, что Monitored Undo Framework полезен. http://muf.codeplex.com/. Он не использует командный шаблон "сверху вниз", но вместо этого отслеживает изменения по мере их возникновения и позволяет помещать делегат в стек отмены, который изменит это изменение.

Я создал это как часть приложения WPF, которое было создано с использованием MVVM. Большинство действий по устранению возникло в нашей модели базового домена, но мы также подключались к некоторым областям ViewModels, чтобы там можно было отменить/повторить.

Дополнительную информацию и документацию можно найти на сайте codeplex по адресу http://muf.codeplex.com/.

Ответ 3

Я предполагаю, что вы связываете шаблон Command с Memento?

Я бы подумал, что система отмены будет каким-то образом интегрирована в уровень ViewModel, так как это состояние пользовательского интерфейса.

?! Обычно отменить/повторить действия на бизнес-объектах, а пользовательский интерфейс отражает бизнес-уровень.

Скажем, у нас есть класс продукта со строкой "Описание". ProductVM предоставляет свойство string, которое вызывает PropertyChanged. При модификации memento хранит экземпляр старой модели. Если вы отмените, восстановите память, используя ProductVM.Description = (memento as Product).Описание: модель будет обновлена ​​и пользовательский интерфейс тоже.

NB: избегайте (memento as Product) только для образца;)