I'm playing with quite simple interface with buttons and shortcuts for adding, inserting and removing rows of datagrid (underlying bound collection). There's also a need to implement undo stack. But...I have no idea how to do with internal logic of this control. By default DataGrid can remove or add new row automatically and performs many other things on user input (Esc,F2 and so on) thus implicitly changing the bound data.
As commands are to be executed on the VM side undo stack is its (or even M's) business either, yet DataGrid contains internally predefined bindings to DataGrid. commands. And I see no easy mean of 'watching' the changes to the data.
My understanding of the ideal MVVM flow is like that: User action (View) -> Command (VM) -> Commmand Excution + Undo stack operations. (VM-M) -> UI changes respectively to VM changes.
I'm confused and need some good advice concerning the implementation.
2 Ways to go about this:
Have your all logic on the ViewModel (POCO Models).
You'll have to have your ViewModel contain an Undo/Redo stack. How you implement it is up to you, but I'd suggest just having the Undo/Redo stacks be of
Tuple<String, Object>
. Store the property name and the value of the property. It's easier than managing clones. It also gives you the ability for a poor mans "dirtiness" check by seeing if the UndoStack has any items on it.Give your models some interfaces such as IUndoRedo (Rich Models).
You'll have to have your ViewModels call interface methods to Undo/Redo, but the idea is the same... have an Undo/Redo stack that is composed of
Tuple<String, Object>
.If you do decide to want to have a rich model approach, you can look at existing frameworks out there such as CSLA.Net which is made for rich models, though it might be a bit more than what you'll really need. Just throwing it out there in case you want to have really rich models.
A side note: You're ObservableCollection (ItemsSource) should be of ViewModels, not Models. Just throwing that out there in case you were using the Models. That is, don't do ObservableCollection<IEmployee>
, but rather ObservableCollection<EmployeeViewModel>
. It makes things easier, much easier and more reusable!
Another side note: try to avoid the DataGrid. It makes developers wanna rip their hair out. I'd just roll out your own "Grid" with ListView :)
Normally I build the undo logic into the models themselves. Get them completely working the way you want before you even start thinking about how they are going to be bound to the UI.
I have done an article about undo / redo in MVVM. It is divided in two parts: the first explains undo / redo in general editions and the second explains working with lists:
Part 1: Using the Viewmodel pattern to provide Undo / Redo in WPF
The flow is: User action (View) -> Command (VM) -> Commmand execution modifies the Model -> Model notifies changes to VM -> VM notifies changes to the view. This way if the model is modified from other source it also refresh the view.
There is also a github project here.
Since your DataGrid is bound to a collection, you can monitor changes to the Collection itself instead of the DataGrid. Use the CollectionChanged
event on your collection to watch for added or removed items, and register a PropertyChanged
event on all of your collection's items for monitoring edits.
An alternative idea would also be to provide a RevertChanges
command instead of UndoChanges
. Its much simpler to implement because you only need to store the original collection so you can restore it if needed.
来源:https://stackoverflow.com/questions/7984674/datagrid-and-mvvm-with-undo-redo