WPF ObservableCollection Edit Mode - wpf

I am using observable collections all around my applications. My problem is that when i use a popup window for editing those entities, my bound lists are getting changed when the user changes those corresponding fields in the window.
How could i simply freeze the observable changes norifications, and release them only when the entity is saved?
Thanks,
Oran

I think the issue is not with the collection, but with the entities themselves. ObservableCollection raises an event when an item is added or removed, not when a property of an item is changed. This part is handled by the INotifyPropertyChanged implemented by the item, so it's this notification you need to disable.
I suggest you have a look at the IEditableObject interface, which is designed for this kind of scenario. You can disable the notifications in the BeginEdit method, and reenable them in EndEdit and CancelEdit.
EDIT: Paul Stovell has a nice implementation of an IEditableObject wrapper here :
http://www.paulstovell.com/editable-object-adapter

You can use:
BoundPropertyOfViewModel = CollectionViewSource.GetDefaultView(AgentDeploymentDetail);
and bind to the view instead of binding directly to the ObservableCollection. This is the same object that allow you to filter/sort your output without touching the collection.
When you want to stop changes, use DeferRefresh(). When you are done, call Refresh().
WARNING
This will not pervent showing of changes in each item itself, only the list.

You could make a deep copy of the object you want to edit. This way, you can act on the copy while editing, without interfering with the original that remains in the list. Once you`re done editing, you can replace the original by the edited version or rollback.

All the anwers above are great. but i found a good and convinent prodedure to perform the desired in an efficient and clean way. It is based on performing a deep copy on a detached object, using Matthieu MEZIL entity cloner ( http://msmvps.com/blogs/matthieu/archive/2008/05/31/entity-cloner.aspx ).
For full details please check out the followings : Entity Framework Attach Exception After Clone
Thanks for all the great support...

Related

DataGrid and MVVM with Undo/Redo

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
Part 2: Viewmodelling lists
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.

Triggering a command in the view model based on an action in the view

I am working with the Infragistics XamDatagrid. When an event takes place in the view (new record updated), I would like to call a method in the view model. I can't just listen to the CollectionChanged event of the source in the VM because that event gets triggered once the user begins editing the new row in the grid. The RecordUpdating event gets called when the user finished the edits.
What I am doing now is accessing the DataContext directly and calling the method. The problem is that there is nowhere I can find to attach a Command to the gird that would tigger on the event I am interested, nothing like the Command of a Button.
Generally, what is the best way to call into a VM from a view when there is no Command parameter I can set in the view?
Code-behind glue is fine in most cases, but if you find yourself gluing the same type of thing repeatedly, you can look into Attached Behaviors. Here's one old-school approach the introduces the concepts, but doesn't use the new Behavior class: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
Here is an example using the new Behavior class, including a demonstration of how it integrates with Blend: http://geekswithblogs.net/lbugnion/archive/2009/04/05/using-a-behavior-to-magnify-your-wpf-applications.aspx

wpf MVVM ObservableCollection

My model has a couple properties one is a string and the other is an observablecolletion. When the model is created it fires off a backgroundworker thread to basically poll a .dll for data. Based on the data it receives it will either set the string and/or add and item to observable collection. My string property seems to fire its Onproperty change just fine and the view updates. However, my observable Collection throws a cross thread exception. I have tried moving code where i set the ObesrvableCollection to the worker.ReportProgress and get the same error. I have moved the logic into the view model and still get the same threading error. I'm unsure why my string property works for one. I have read about Dispatcher.Invoke, but i'm pretty sure that my model should not be aware of this. Can anyone explain the correct way to go about this please.
Just fyi - my view is not tied directly to my model. I have a property for my model in my viewModel and the model gets passed through constructor injection. Just want to put that out there before anyone thinks my model is talking directly to the the view.
Hard to give specifics without code. However, WPF automatically marshals property change notifications for scalar properties but not collections. Hence, you must be modifying the collection from a non-UI thread.
There is no reason why your VM can't use Dispatcher, or perhaps the more generic SynchronizationContext if you prefer. It can make things more tricky to test, however.
If you post code there may be a way you can simplify things.
As Kent said, if you're not on the UI you need to use the Dispatcher to update your collection:
Application.Current.Dispatcher.Invoke(new Action(() =>
{
// update your ObservableCollection here
}));

How do I Stop WPF DataContext Failure After Changing an Object Reference?

Using MVVM, my viewmodel is my WPF window's data context. In the viewmodel is an observablecollection that holds the items in a listbox. When I add items to the collection, the listbox updates as expected
As part of my generic undo function, the observablecollection can be replaced with an older version. This happens by passing the collection to a method by reference and changing the reference. Everything after the undo works correctly except for the listbox. This continues to show data from the old reference.
How can I either stop this from happening or change the reference that the datacontext uses so that my listbox is "undone" and then continues working?
You need to provide some change notification in order to trigger the UI to update which you won't get from reassigning a ref variable to another instance. To get the notification you can either Clear and re-fill the original ObservableCollection instance or fire a PropertyChanged event for the collection property's name after swapping the instances.
Using the MVVM pattern, properties are changed in the ViewModel, with setters raising the PropertyChanged event. The event is handled in the View (automatically by WPF) and bindings are refreshed.
In your case, the value is being changed without the setter being called, so the PropertyChanged event (if it exists) is not being raised.
One option might be to manually raise the PropertyChanged event from the undo code. This would allow you to keep your existing design (please note that INotifyPropertyChanged.PropertyChanged is different to ObservableCollection.CollectionChanged - do a bit of research if this is not clear).
The second option would be to handle the CollectionChanged event, and keep a record of ItemsAdded and ItemsRemoved.
Your undo mechanism can then re-add any items which were removed, or remove any items which were added. This might require a bit of design tweaking.
I would go with the second design, as I think the design rethink might be a good idea. If you are using MVVM, you should be using Commands, and if you are using Commands you can implement Undo/Redo functionality neatly by extending the Commands (remember that an action made by a user is usually a bit more than a value change).

WPF data-binding manual update

I have a List<Foo> from a non-WPF assembly which I'm attempting to databind to a WPF <ListBox>. Initially, the list items display correctly, but when I add a new item to the List<Foo>, the listbox doesn't add a list item. How do I tell the list box to re-bind / update / refresh the data and show the new item?
Although using an ObservableCollection is the best way, to answer the actual question, the way to update manually is to call BindingExpression.UpdateTarget
You should use a ObservableCollection instead, then you'll get updates automatically.
Thanks for posting this answer. Even if you use ObservableCollection, you may need to use BindingExpression.UpdateTarget. This can be the case if the collection is not in the UI thread. I've been writing some multi-threaded WPF apps, and I've been finding myself having to strip out data binding when I move model code to another thread, because I can't count on the update system to really work. While I find data binding to be a great concept, I think the opaqueness of the data binding system has been a real hindrance for my adoption of it. (Sorry for the rant!) Thanks again, Adam.

Resources