views:

130

answers:

1

I'm working on a Silverlight LoB app which the designers want to have a tabbed-interface, similar to the interface of Visual Studio (we'll probably use the Telerik Rad controls for docking tabs). Having done a prototype, the interface is working well so far, but I'm having problems thinking of how to implement undo/redo functionality in an MVVM project.

The undo/redo functionality has to:

  1. On undo/redo, restore the UI state, i.e. return focus, selection etc. to the control(s) (such as a textbox) that the change originated from.
  2. Have a per-view undo/redo stack

Typically, I'd use the command pattern, but I'm not sure how to apply that with MVVM.

I've used commanding & binding to get the idealised loose-coupling of the views & view-models, but it makes undo/redo a lot trickier, since the view-model doesn't have any concept of the view and the state of the view when a command is received or a bound property is changed. It seems that I need some kind of service tracking which view is active whenever the user performs some undoable action and gets the state for later restoration.

Is there any consensus on what is the best-practise for implmenting undo/redo in MVVM? I've looked with interest at how Daniel Vaughan does it in his Calcium project; Blend was apparently written using the MVVM pattern and it behaves just as I want my app to, it'd be great if MS explained how they did it!

+1  A: 

The first thing you need to do is make sure you separate the actions completely from the interface. That means turning all operations that affect data into discrete actions. That also implies that whatever causes a change of view should also be recorded as a discrete action. Basically the state of the interface should only reflect the data-changes and command-based view changes (see my last note below about view changing though).

The most successful undo systems we have used previously allowed nesting of IUndoableCommand objects. These compound commands roll up into a single user action (the sort of action you would expect to see display in an Undo menu).

I note you mention using undo across views... That seems like unusual behaviour for a multiform app. Normally undo is only within individual controls and for any drag-drop operations. The exceptions are usually graphic based interfaces (not forms-based). Changing form during undo would be the equivalent of MS Word switching to another document and continuing to undo... quite disturbing for the end user. Might want to get the user experience guys to rethink that aspect of the design. Just my 2 cents worth.

Hope this helps.

Enough already
I will now also be investigating the Calcium project based on your mention. Thanks for that.
Enough already
Thanks for your answer, HiTech. When you say "undo is only within individual controls" do you mean that, for example, on a form (view) with a few TextBoxes, the user has to focus on a TextBox where a change originated for him in order to undo/redo any change he made via that TextBox? Separating the actions completely from the interface - wouldn't that mean forbidding TwoWay binding between controls in the view and properties on the view model? Unfortunately, our app has both an interactive drawing surface (canvas) it's complex.Definitely appreciate your thoughts!
JamesCo