How to implement undo/redo in a MVVM application?
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:
- 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.
- 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 impl开发者_运维百科menting 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!
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.
@JamesCo,
I've implemented undo / redo for a WPF application and ended up publishing my undo / redo code to http://muf.codeplex.com/. You can also get it via NuGet. Just look for "MUF" or "Monitored Undo Framework". It includes support for Silverlight 4.0, as well as .NET 3.5, 4.0, and WP7.
My WPF app also used MVVM and, in some cases, did allow undoing things like selection changes. I also tracked the active "page" that was shown in the WPF Frame so that the user was moved back to the page where the undo action should apply.
The library takes a flexible approach as to how you code up the action to undo / redo each step. Ultimately, it just takes a delegate for undo and a delegate for redo. You can have these delegates do whatever you like. Included in the library is a default implementation that simply takes the object, the name of the property, the old value, and the new value. It constructs reflection based delegates that will apply the old or new values as needed.
As far as isolating the changes for each view, the library allows you to keep a separate stack of undo / redo actions for each "document" or "container". You simply pass a reference to the container in order to get the associated undo / redo stack.
Lastly, the library includes support for batching changes together. This is useful in times where multiple actions should be undone together as a unit.
Comments and questions are welcome on the codeplex site (http://muf.codeplex.com/). You'll also find complete documentation and sample apps there.
精彩评论