Вот решение, которое я использовал для своего проекта. Решение оказалось отлично работает.
Система использует объекты событий отмены, где каждое событие отмены знает, как отменить и повторить себя.
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 (синглтон проекта), в котором хранится стек отмены и стек восстановления.