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.
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?
I am trying to use LINQ to select data from database and bind it to DevExpress GridControl. I have succeeded yet though. here is my code below. It doesnt bind anything, how can I fix this???
public ucEmailList()
{
InitializeComponent();
ARYADA_EMAIL_DBEntities db_ = new ARYADA_EMAIL_DBEntities();
var elements = from element in db_.TEST_EMAILS
where element.ID >4
select element;
gc.DataContext = elements;
}
Use ItemsSource
gc.ItemsSource = elements.ToList();
Anyway this is poor idea. Why don't you use ViewModel that provides data from a database and bind it in a XAML to proper property of GridControl?
EDIT - the way I would do it
Remove the code from code behind that initializes gc
Provided you have somewhere in your XAML a GridControl you should also have its property bound:
<dexp:GridControl //.... some properties
ItemsSource={Binding Data} />
Inside view initialization (see point 1) set DataContext to you view model that contains property Data which actually gets data from a repository.
Make sure data are not directly objects from the database. Remap them to your Domain objects. To save some time you can use AutoMapper.
For your scenario, you can just change one line:
gc.ItemsSource = elements.ToList()
ToList() is required because elements has yet to be calculated and the control does not like binding to it.
For reference, elements was defined as:
var elements = from element in db_.TEST_EMAILS
where element.ID >4
select element;
Do you want to know more?
Later, if you are interested in the mvvm pattern for wpf I recommend this article:
Implementing the MVVM Pattern Using the Prism Library 5.0 for WPF
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);
}
}
I currently have a GridView populated with ItemsSource binding to an ObservableCollection.
It's showing the items as I intended but I'd like to manually add one more item at the end that looks and behaves differently. Is there a way I can do it without modifying the ObservableCollection the ItemsSource is bound to?
There are two approaches to this:
Using MVVM the original collection would be pulled up from the model layer and the additional item would be added in the view-model. It works as you can consider the additional item required only for the view, e.g. for example a list of poll options where you need to add an 'all of the above'.
Have two collections, separate, and then concatenate/join them together using an IValueConverter when you bind the ItemsSource. This approach is more tricky and only really useful when you need both collections separate and combined, e.g. a list of items in one collection and a list of item categories in another but also combined.
I have successfully used both approaches in a large widely-used WPF application.
Edit: Reading the comment on the question, if you did mean that you want a different visual or behaviour of the additional item you can achieve that using an Selector DataTemplateSelector and/or ItemContainerStyleSelector. I recently used a combination of option #2 and template selecting successfully.
Some options:
Use a CompositeCollection, you can define this is xaml. Not sure if it would solve the issue, you might need to play around with it a bit
Create a new collection behind the scenes that is populated with the items from your collection + the extra item.
Write a converter than creates a new collection (or returns an IEnumerable) with the new item added.
I guess all of these options have a common theme in that they are all a new collection. You basically can't bind directly to the original collection because it doesn't have the extra item, so you have to bind to something else in some form.
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.