Friday, January 30th, 2009...1:00 am

Using the Viewmodel pattern to provide Undo / Redo in WPF

There are a lot of articles about data validation with the  mvvm pattern and also it is clear how to do UI test in a natural way.

Here I want to analize the problem of how to provide an undo / redo in this pattern. There are very few articles about undo / redo in wpf, and I think that is because the databinding way of working seems at first difficult to adapt to undo / redo.

I will try to show a (at least for me) very natural way of provide undo / redo in wpf within mvvm.

Generalities: The UndoItem class

If we want some editing action to be undoable we need to collect some information of the previous state so we can return to that state when we do an Undo.

For that, all the undoable editions doesn’t modify the target object directly and instead create some “UndoItem” object with the editing data that will be submitted to the object.

More exactly, each edition has to belong to some Undo / Redo scope, which I will call a Project. Each project will have his own Undo/Redo history, and all the editions will be submitted to the project. In most cases there will be only one project.

The base class UndoItem will have the following methods:

public class UndoItem
{
  public virtual bool Execute() {}  //False if there were errors
  public virtual void Undo() {}
  public virtual void Redo() {}
}

Every kind of edition needs to define his own class derived from UndoItem with his editing and state data and his implementing the UndoItem methods.

But, how many of these classes we will need?

One may think that we will need to define lots of them, but in fact we only need these:

  • one for editing properties: UIEditProperty. Corresponds to INotifyPropertyChanging
  • one for editing collections: UIEditList. Corresponds to INotifyCollectionChanged .

Enters the ViewModel

We will show now how the UIEditProperty works. In the following article we will study the list case.

In the mvvm pattern the view gets and sets data from viewmodel objects properties. The idea that I want to apply is that when the set method of some viewmodel property is called, the model will not be directly modified, and instead we will create some undoitem object with all the editing data that will be submitted to the project.

A typical implementation of that will be:

public string Name
{
  get { return _resource.Name; }
  set { EditProperty(_resource, "Name",value); }
}

where  EditProperty is the following method:

private void EditProperty(string propStr, object value)
{
  UIEditProperty op = new UIEditProperty();
  op.ModelObject = Elemento;
  op.PropertyStr = propStr;
  op.Value = value;
  _proy.Submit(op);
}

In the submit, the project will annotate the previous state of the property in another field of the undoitem and next it will assign the value to the model object (using reflection to obtain the property).

But, how does the view gets notified of the edition? Remember that the databinding was between the view and the viewmodel, and here we have modified the model object.

Databinding the viewmodel to the model

My solution for that is to create a databinding between the viewmodel and the datamodel so the viewmodel gets notification of any change in the model.

This works in this way: the viewmodel object has a link to his model object  (or objects, the relation is not neccesary one to one!). When the viewmodel creates his link to the model object it subscribes to the model object property change event.

When the viewmodel receives a notification of some change in the model, what it does is to re-send the notification. This new notification is seen by the view.

The following points must be remarked:

  • the model object is not aware of the view and the viewmodel
  • the model object can be modified by other ways different from the viewmodel, but in any case the viewmodel will be modified (and after the view).
  • we can have more than one viewmodel object pointing to the same model object. If someone modifies it all will see the change.

In short each viewmodel object redirects modifications to the model via undoitems, and  the changes in the model are notified to the viewmodel which then forwards the notification to the view.

The exact implementation will depend in the relation between the viewmodel and the model object.

public object ModelObject {
  set
  {
      _modelObject = value;
      _modelObject.PropertyChanged +=
         new PropertyChangedEventHandler(OnResourcePropertyChanged);
  }
  ...
}

and the implementation of OnResourcePropertyChanged simply sends the notificaciont to the view:

public void OnResourcePropertyChanged(object sender,
   PropertyChangedEventArgs e)
{
    SendPropertyChanged(e.PropertyName);
}

Conclusion

You can say against this pattern that you have to write a lot, as you are writing every property twice: one for the model and one for the viewmodel.

That is not true because the viewmodel in most cases will have a number of differences with the model. In any case what you get in change is important enough to consider do that extra work. And I not only refer to undo items, because this separation is what you need to do data validation, UI test, etc.

In the next post we will explain the quite more complicated situation of the lists editing.

Leave a Reply