Отменить внутри 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) только для образца;)