tags:

views:

849

answers:

2

In my previous projects, I have already implemented undo system in c++, and I know how it work. I am also aware of the Command pattern.

I will be implementing a C#/WPF desktop application and would like to base my design on the M-V-VM pattern.

The application will:

  • be relatively a small project (2-3 weeks estimated work for 1 dev)
  • have a simple data model with persistence (linq to XML)
  • support undo/redo

I was wondering if anybody has experience with implementing an undo system when following the M-V-VM pattern. How would it fit in it? How can it benefit from the INotifyPropertyChanged and INotifyCollectionChanged notifications so minimal work is required when implementing the Models (business objects).

I would think the undo system would some kind of integrated into the ViewModel layer, as it is a UI state.

Any thought?

A: 

I suppose you are coupling the Command pattern with a Memento ?

I would think the undo system would some kind of integrated into the ViewModel layer, as it is a UI state.

?! Usually, undo/redo acts on business objects, and the UI reflects the business layer.

Say we have a Product Class with a "Description" string. The ProductVM exposes a string property which raises PropertyChanged. On modification, the memento keeps the old model instance. If you undo, restore the memento using ProductVM.Description = (memento as Product).Description : the model will be updated and the UI too.

NB : avoid the (memento as Product), just for the sample ;)

rockeye
+2  A: 

Here is the solution I used for my project. The solution proved to be working perfectly.

The system is using undo event objects, where each undo event know how to undo and redo itself.

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

I was able to build the system by implementing only 2 undo events: One for property changes; one for collection changes.

The idea is that those events implement the Undo/Redo by modifying the model directly.

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)
 }
}

The ViewModel take care of creating undo events by calling ViewModelBase functions:

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); }
    }
}

Finally, there is a UndoManager (project singleton) that stores the undo stack and the redo stack.

decasteljau