Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

You may find the Monitored Undo Framework to be useful. http://muf.codeplex.com/. It doesn't use the "top down" command pattern, but instead monitors for changes as they happen and allows you to put a delegate on the undo stack that will reverse the change.

I created this as part of a WPF application that was built using MVVM. Most of the undo actions originated in our underlying domain model, but we also hooked into some areas of the ViewModels to allow undo / redo there too.

You can find more info and documentation on the codeplex site at http://muf.codeplex.com/.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top