When binding an enumerable to an ItemsSource or similar, the binding uses the enumerable's default view, which I know you can get using the following code...
var defaultView = CollectionViewSource.GetDefaultView(someCollection);
This has worked great for us, allowing us to, for instance, add sorting right to the default view of various collections.
However, we have a specialized Collection class which requires a specialized ListCollectionView subclass to work properly. That said, how can we change its default view so that is returned when someone binds directly to the collection?
For a work-around, we created a new property called MainView which holds our custom ListCollectionView, then we bind things like ItemsSource to that, but that means consumers of our collection have to be explicitly told not to bind directly to the collection but rather to the MainView property or they'll get the default view. Considering the standard is just to bind to the collection directly, that's a potential issue we're trying to avoid.
So again, how can I specify my own ListCollectionView subclass as the default view for our custom collection?
On your collection, implement the interface ICollectionViewFactory. Here is an example:
public class MyCollection : ObservableCollection<MyItem>, ICollectionViewFactory
{
public ICollectionView CreateView()
{
return new MyListCollectionView(this);
}
}
Related
I'm familiar with the concept that a collection used for a WPF Binding (for example, ItemsSource) actually uses the default collection view for that collection, but I am looking for some clarification on where that happens. Potentially depending on that answer, what is the recommended way to implement similar binding behavior if I am creating my own UserControl that has its own property like ItemsSource and I want to use ICollectionView functionality on it within my UserControl?
So if my UserControl has a ItemsSource property like:
public static readonly DependencyProperty TestPropProperty =
DependencyProperty.Register("TestProp", typeof(System.Collections.IEnumerable), typeof(MyUserControl),
new FrameworkPropertyMetadata(null, TestPropChanged, null));
And my XAML uses it like this:
<local:MyUserControl TestProp="{Binding MyCollection}"/>
Let's say MyCollection is of type ConfigCollection, my own type, derived from ObservableCollection. From MyUserControl's perspective, the binding sets TestProp to an object of type ConfigCollection (NOT a Collection View object).
The MS docs: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0#binding-to-collections
Under "Using a default view", state:
WPF also creates a default collection view for every collection used as a binding source. If you bind directly to a collection, WPF binds to its default view.
That seems to be pretty universal, so in my case I was thinking it might be set as a Collection View whose source was MyCollection, but that is not the case.
So, does the "magic" of translating a collection into its default collection view all happen within each control's code? Is there a recommended way of making sure my ItemsSource can accept either a collection (and use the default view) or accept a Collection View and then use that specific view rather than the default?
As far as I know, the default way to use a ObservableCollection that is bound to a listview is with model classes as elements (ObservableCollection<MyModel>). So when a listview element is selected, we use NavigateAsync and pass the model, which then can be used by the ViewModel to "fill itself".
The problem with this approach is, that it's not possible to use ViewModel properties for binding in the listview.
For example:
I have a View, ViewModel and Model "PickList", which contains a collection of "PickLine" objects - each having a View, ViewModel and Model themselves. The PickLine object contains a property "PickedQuantity" and a property "OpenQuantity". Now in my PickList view, I don't want to bind these two to separate items (e.g. two labels), but I want to have one label to display both I a format like for example "PickedQuantity / OpenQuantity". I know this example can be solved by using multi binding or something like this. But that's not the meaning of it all.
My PickLine ViewModel already has a property "QuantityString", that I want to bind to the label of a listview element via DataTemplate. But how can I do this. Is it even possible?
Make a property that combines the two other properties and bind to that. E.g.:
public string FullQuantity {get {return $"{PickedQuantity} / {OpenQuantity}";}}
Then in the setter for PickedQuantity and OpenQuantity, you will want to call whatever PropertyChanged method you have set up to notify the bindings of a property change and pass in the FullQuantity property name so elements that are bound to FullQuantity get updated when either PickedQuantity or OpenQuantity are changed.
This way, you are only binding one label's text to one property and that label would get updated when either of the two quantity properties are changed.
Note: I am unfamiliar with Prism, but this approach should work regardless of the Mvvm framework in use.
Your PickListViewModel should expose a collection property whose items are of type PickLineViewModel (not PickLine).
Whether you need an ObservableCollection<PickLineViewModel> depends on where changes can happen - in service / model that initially created the PickLines or in the GUI or both. In any way, you have to make sure the changes are propagated from one side (the collection of view models) to the other (the collection of models). Google wrapping observable collection as a starter (hint: avoid two-way sync if possible). These blog posts are old but still relevant and make a good reading. A trivial wrapping is described in this answer.
The documentation of the CollectionView class says:
You should not create objects of this class in your code. To create a collection view for a collection that only implements IEnumerable, create a CollectionViewSource object, add your collection to the Source property, and get the collection view from the View property.
In other words, I am supposed to write:
var cvs = new CollectionViewSource();
cvs.Source = myData.Where(d => someCondition(d));
var view = cvs.View;
instead of
var view = new CollectionView(myData.Where(d => someCondition(d));
However, the documention fails to explain why I should do that. Both options seem to work.
What bad things will happen if I choose option 2 over option 1?
There are a few specializations of the CollectionView class, like for example ListCollectionView. Whenever you bind to some collection, there is a view automatically generated for you based on the type of the source collection. Instead of explicitly creating a CollectionView or a CollectionViewSource, you can get the default view of a collection by using the static CollectionViewSource.GetDefaultView method:
ICollectionView view = CollectionViewSource.GetDefaultView(myData);
It will return a ListCollectionView if myData implements IList.
It's perfectly fine to expose an ICollectionView from a view model if you perform the filtering or sorting in there. A CollectionViewSource is mainly used when you want to perform the filtering, sorting or grouping of a source collection in the view.
Binding CollectionView may present problems down the road if you want to change the way your data is displayed.
From the CollectionViewSource documentation:
Because a view does not change the underlying source collection, a source collection can have multiple views associated with it. By using views, you can display the same data in different ways. For example, you can use two views on a collection of Task objects to show tasks sorted by priority on one part of the page and grouped by area on another part of the page.
Binding directly to a single CollectionView limits the ways you can display your data.In short, CollectionViewSource plays nicer with your collections in XAML when binding.
I have a wpf application and im using winform host.
In some cases the 'SelectedItem' property is complex type that have a property from type 'List'.
How can i know from code if the collection changed? Or just know of the custom editor opened?
You can use a ObservableCollection<T> instead of a List to have the functionality of indicating changes to the contents of the list.
The ObservableCollection implements the interfaces INotifyCollectionChanged and
INotifyPropertyChanged to archive this behaviour.
More information to ObservableCollection on MSDN:
https://msdn.microsoft.com/de-de/library/ms668604(v=vs.110).aspx
If I understand well you have collection of elements, and each of them have some other collection.
In that way you could also consider using BindingList as your base collection - it implementsIRaiseItemChangedEvents - Indicates whether a class converts property change events to System.ComponentModel.IBindingList.ListChanged
I have two properties of ObservableCollection<string> type (in separate projects); What I want to do is to bind these two using reflection and SetBinding like this -
//Get the PropertyDescriptor for first collection property
PropertyDescriptor relatedPropertyDesc = prop.Find(firstCollPropName, false);
Binding relatedPropBinding = new Binding(relatedPropertyDesc.Name);
relatedPropBinding.Source = this.SelectedItem;
relatedPropBinding.Mode = BindingMode.TwoWay;
//Bind the second collection property using binding created above
propItem.SetBinding(MyItem.SecondCollProperty, relatedPropBinding);
This SecondCollProperty is then bound to a ComboBox's ItemsSource.
As such this works correctly, values present in firstCollProperty are displayed correctly in combobox; but if some changes are made in firstCollProperty at run time then they are not reflected in ComboBox!(adding new items or creating new collection object).
Changes are reflected correctly after refreshing the binding(again executing the above code).
My question is - If two ObservableCollections are binded together why any changes in first doesn't get reflected in other? but same thing works for properties of string or double type.
Is there any way of achieving this?
Just going through some old unanswered questions and saw this. Undoubtedly you've come up with a workaround by now, but my recommendation would be look into something like CLinq, Bindable Linq, or Obtics for this. See this question for more details. You'd take the first collection, create a dynamic query against it, and expose that dynamic query (which implements IObservableCollection) as your second property.