How does binding to collections really work? - wpf

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.

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.

WPF: Which collection class to use

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);

ObservableCollection + ICollectionView = ObservableCollectionView?

I have a View Model that exposes an ObservableCollection. Now I want to add grouping and therefore want to use a View class, like e.g. the ListCollectionView. But that is not a generic type. Also, if I construct a ListCollectionView from an ObservableCollection, then it is not updated when the ObservableCollection is.
Did someone implement an ObservableCollection-backed View-collection somewhere?
If not, is it possible to achieve this dynamism somehow by using XAML?
You don't need a specific implementation ; a CollectionView is just a view of a collection. You can get the default CollectionView for a collection using CollectionViewSource.GetDefaultView:
ObservableCollection<Something> collection = ...
ICollectionView view = CollectionViewSource.GetDefaultView(collection);

How to keep a data-bound list reverse-sorted

I have a listbox bound to a collection. I would like the ListBox to always reverse the order of the items. This handler--hooked up to the control's load event--works for the initial load, but not thereafter. Ive tried using the SourceUpdated event but that doesnt seem to work.
How do I maintain a constant active sort?
MyList.Items.SortDescriptions.Add(New SortDescription("Content", ListSortDirection.Descending))
How is the collection stored that supplies the items for the ListBox? It should be a collection that supports INotifyCollectionChanged. The framework provides ObservableCollection<T> which you can use.
In the constructor of your ViewModel (or wherever the collection lives), you then get the DefaultView for adding the SortDescription. The CollectionView is like a layer on top of your collection, which you can use to sort, group, filter, etc. the items without actually affecting the underlying data source. The framework creates a default one for you. To get a reference to it, you can use code similar to the following:
var collectionView = CollectionViewSource.GetDefaultView(Widgets);
if(collectionView == null)
return;
collectionView.SortDescriptions.Add(new SortDescription("Content", ListSortDirection.Descending));
With that in place, you should be able to add items to the ObservableCollection<T> and the sort order will be maintained.
If your source collection is a List<T> or some other collection that doesn't implement INotifyCollectionChanged, there is no way WPF can detect when an item is added. You need to use a collection that implements INotifyCollectionChanged, like ObservableCollection<T>.
Also, the items in your collection need to implement INotifyCollectionChanged so that changes to the items are taken into account

Resources