WPF: Which collection class to use - wpf

After spending a whole day trying different suggestions, I'm back at square 1. I'm trying to bind my view, a XAML Window, to one of my ViewModel properties, say, SalesOrders. The ViewModel in turn talks to the Model (an EF Model on top of a database). The question I'm facing is the collection type that I should use to expose my SalesOrders property.
I have tried the following types, none of which does all of what I need.
List<T>
ObservableCollection<T>
BindingList<T>
CollectionViewSource on top of the above
Here's what I need my collection to do:
The view has Previous/Next buttons, so the collection should provide some sort of currency manager.
There's a Save button in the view, which I need to get enabled/disabled immediately based on whether the SalesOrder collection has any changes. Since SalesOrder is already an EF type, all of its fields implement INotifyPropertyChanged.
CollectionViewSource provides me with navigation methods (previous/next) but doesn't listen to PropertyChanged events, so modifying data in the view doesn't turn the Save button on. BindingList can listen to PropertyChanged events, but doesn't provide navigation methods. ObservableCollection lacks both functionalities.
TIA.

Why don't you use ObservableCollection<T> then subscribe to the CollectionChanged event to enable or disable your save button as outlined in the answer of the thread MVVM ObservableCollection Bind TwoWay.

According to MSDN about CollectionView here:
In WPF applications, all collections have an associated default
collection view. Rather than working with the collection directly, the
binding engine always accesses the collection through the associated
view. To get the default view, use the
CollectionViewSource.GetDefaultView method. An internal class based on
CollectionView is the default view for collections that implement only
IEnumerable. ListCollectionView is the default view for collections
that implement IList. BindingListCollectionView is the default view
for collections that implement IBindingListView or IBindingList.
Which means you can use BindingList for SalesOrders and bind it in the View, then to manage the navigation you can access its automatically created CollectionView from the ViewModel with:
myCollectionView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(this.SalesOrders);

Related

View Updating Model directly In WPF MVVM instead of ViewModel

This is the link i am following for learning MVVM in WPF and i have a question:-
https://www.tutorialspoint.com/mvvm/mvvm_first_application.htm
DataContext of the WPF window is set to a VIEWMODEL object.
Itemsource of a List-DataTemplate is set to a List from the same VIEWMODEL Object.
The Model contains an implementation of INotifyPropertyChanged.
When i update the view,the INotifyPropertyChanged of MODEL gets fired ie VIEW is directly updating the MODEL while what i have understood till now is that VIEW interacts with the VIEWMODEL only via Bindings and Commands and never with the MODEL DIRECTLY.It is always the ViewModel which interacts with the Model to fetch data for the View.But here,the View is updating the Model DIRECTLY.This is confusing me owing to my limited knowledge.
Please guide.
If the view model exposes the model through a property, the view may actually bind directly to the model through this property. This doesn't really violate the MVVM pattern and is perfectly fine, especially if the model class implements the INotifyPropertyChanged interface. If it does, you can say that the model is kind of a (child) view model.
A "real" model such as a domain object or a service shouldn't have any knowledge about WPF and how you raise change notifications to a view. Therefore it rarely makes sense to bind directly to such objects but if your models are "WPF aware" and implements view related interfaces, you can bind to them without any issues.
It is a common error to bind the Model thru lists to the View. The correct approach would be always to create a ViewModel of that Model (the list element) and bind to it.
For example:
Otherwise you are opening the door to including data on ModelB that should be stored in the ViewModelB.

WPF - How do I access the number of errors in the viewmodel?

I have a single viewmodel representing my applications main view. This viewmodel contains an ObservableCollection of model objects which a grid on my view is bound to. Each item in the collection implements IDataErrorInfo which facilitates the standard WPF validation mechanism i.e an error in the model causes the view control to highlight red (in this instance a cell).
My problem is, I want to perform a piece of logic in the viewmodel which needs access to the number of errors on the page (or rather IF there are errors or not). I can't see a way to access this from within the viewmodel itself, all the WPF validation seems to be contained in the view, is this right?
I don't think there is anything "built in" to achieve this. One option is to expose a property on each model in the OC such as IsValid, which you would set from within the model's IDataErrorInfo indexed property. Your VM can then use a bit of Linq to check for any models in the OC where IsValid=false.
If you are intending to have your VM expose some kind of "PageHasErrors" property, then you'll probably need to have the VM subscribe to each model's PropertyChange event (specifically the IsValid property), assuming the model implements INPC. Within the VM's event handler you would then update the "PageHasErrors" property based on whether any of the models IsValid=false.

How does binding to collections really work?

Well, I'm confused.
If my control has dependency property ItemsSource of IEnumerable type and user binds collection to it what object do I have in DependencyPropertyChangedEventArgs.NewValue?
As far as I know CollectionView is implicitly created for collections and I expect args.NewValue to be of type ICollectionView.
From this blog:
When a user binds a WPF property to a collection of data, WPF
automatically creates a view to wrap the collection, and binds the
property to the view, not the raw collection. This behavior always
happens, and is independent of CollectionViewSource.
But debugger (VS 2012, .net v.4.0) shows me that I receive original raw collection in NewValue. (BindsDirectlyToSource is not set and equals false by default)
How can this be?!
I cannot understand how in this case WPF controls support sorting, grouping and filtering.
How and when is CollectionView injected and used?
Maybe the following extract from the Remarks section in CollectionView answers your question:
In WPF applications, all collections have an associated default
collection view. Rather than working with the collection directly, the
binding engine always accesses the collection through the associated
view. To get the default view, use the
CollectionViewSource.GetDefaultView method. An internal class based on
CollectionView is the default view for collections that implement only
IEnumerable. ListCollectionView is the default view for collections
that implement IList. BindingListCollectionView is the default view
for collections that implement IBindingListView or IBindingList.
Alternatively, you can create a view of your collection in Extensible
Application Markup Language (XAML) by using the CollectionViewSource
class and then bind your control to that view. The
CollectionViewSource class is the XAML representation of the
CollectionView class. For an example, see How to: Sort and Group Data
Using a View in XAML.
So if you do not explicitly bind to a CollectionViewSource, a collection binding is always made to the original collection (what you get in NewValue), but access to the collection (e.g. get an item by index) is always done through the default view. Therefore the statement "binds the property to the view, not the raw collection" is not exactly true.
A quick test revealed that GetDefaultView returns a System.Windows.Data.ListCollectionView for my bound ObservableCollection.

List<> collection does not update the View in MVVM

I used the List<Person> collection as the ItemsSource for the DataGrid control.
But it did not update the View if i remove the item from the List collection. I was struggling for a long time for the solution.
Then Instead of the List<Person> collection in my ViewModel. I changed this into ObservableCollection<Person> collection. Now it updates the view when ever there is a change in the collection.
I am not sure why it updates only for ObservableCollection<Person> ? Anyone ?
Well its in the name. A simple List doesn't tell the ui to update, in other words "the view can't observe the list". There is no weird magic behind Databinding. WPF and DataBinding needs the Data model to tell them "this is new" or "this is changed", you propably already saw INotifyPropertyChanged, INotifyCollectionChanged is the same but for collections, and the List<> doesn't implement it, ObservableCollection does.
Because List does not implement INotifyCollectionChanged
Because the update of a databinding is not a kind of magic, there are several requirements to make databinding working correctly. If you have a single property to bind on this property must be either a dependency property or its parent class must implement the INotifyPropertyChanged interface to notify the wpf binding system about changes of the property value.
For a collection there is a simelar mechanism: it must implement INotifyPropertyChanged to inform the wpf binding system about removed/moved/added items.
See here for more details: http://msdn.microsoft.com/en-us/library/system.collections.specialized.inotifycollectionchanged.aspx
ObservableCollection<T> fires change events every time you change the items in the collection. List<T> doesn't. That's the reason.
DataBinding is lazy. If you don't tell your view that something has changed, it won't bother updating. Under the hoods, WPF DataBinding registers for change notifications, so that your ViewModel can tell the view when it has changed. It does this with interfaces like INotifyPropertyChanged and INotifyCollectionChanged.
ObservableCollection<T> implements the interface INotifyCollectionChanged. This interface defines the event CollectionChanged, which your View basically attaches its own event handler to. That handler will update the view when the event is raised by the collection.

WPF MVVM: Notifying the View of a change to an element within an ObservableCollection?

Calling OnPropertyChanged for an ObservableCollection only works when there has been some change to the properties of the collection, not the objects it contains (add, remove, clear, etc).
Is there any way to notify the View that there has been a change to an item within the collection?
The objects it contains have to implement INotifyPropertyChanged as well. In your setter you trigger the event, and WPF will pick up on this and read in the new value as long as you are using two-way bindings or read-only bindings.

Resources