I have lots of entities with nested List<> in each.
For example, I have BaseEntity which has List<ColumnEntity>.
ColumnEntity class has List<Info> and so on.
We are working with a WPF UI, and we need to track all changes in every List of BaseEntity. It is implemented by instantiating a new ObservableCollection based on the needed list, and with binding to that ObservableCollection.
What are the pros and cons changing all these nested Lists to ObservableCollections? So we can track all changes in BaseEntity itself without reassigning each list of BaseEntity to modified bound ObservableCollection?
Assuming that methods specific to List are never used.
Interesting question, considering that both List and ObservableCollection implement IList<T> there isn't much of a difference there, ObservableCollection also implements INotifyCollectionChanged interface, which allows WPF to bind to it.
One of the main differences is that ObservableCollection does not have AddRange method, which might have some implications.
Also, I would not use ObservableCollection for places where I know I would not be binding to, for this reason, it is important to go over your design and make sure that you are taking the correct approach in separating layers of concern.
As far as the differences between Collection<T> and List<T> you can have a look here
Generic Lists vs Collection
It depends on exactly what you mean by this:
we need to track all changes in every List of BaseEntity
Would it be enough to track changes to objects already in the list? Or do you need to know when objects are removed from/are added to/change positions within the list?
If a list will contain the same items for their whole lifetime, but the individual objects within that list will change, then it's enough for just the objects to raise change notifications (typically through INotifyPropertyChanged) and List<T> is sufficient. But if the list will contain different objects from time to time, or if the order changes, then you should use ObservableCollection<T>.
So while the differences may be interesting (and a previous poster has already covered those), typically you won't have that much of a choice - either you need ObservableCollection<T> or you don't.
List represents a strongly typed list of objects that can be accessed by index. It provides methods to search, sort, and manipulate lists. The List class is the generic equivalent of the ArrayList class. It implements the IList generic interface using an array whose size is dynamically increased as required.
ObservableCollection is a generic dynamic data collection that uses an interface "INotifyCollectionChanged" to provide notifications when items get added, removed, or when the whole collection is refreshed.
Read more about it in this link: http://www.codeproject.com/Articles/42536/List-vs-ObservableCollection-vs-INotifyPropertyCha
One more important difference is you can access ObservableCollection only from thread on which it was created where as list can be accessed fromany thread.
I see no problem with that, other than a very marginal performance overhead.
Note that if you modify the internal Lists directly, you are not notified about changes. Also if the objects which are contained in the ObservableCollection are modified you are not notified. Notification occurs only, if elements are added, replaced, removed or moved.
Related
I try to implement filtering in my own observable collection.
My approach is following:
I assume the control using ItemsSource should call IEnumerable.GetEnumerator() on my collection to get the items it should render. So I define my own IEnumerable.GetEnumerator() to apply filtering.
Here's relevant code:
public Func<T, bool>? Filter { get; set; }
public void Refresh() {
OnCollectionChanged(
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Reset
)
);
}
IEnumerator IEnumerable.GetEnumerator()
=> Filter is null
? (IEnumerator)BaseIEnumerableGetEnumerator.Invoke(this, null)!
: this.Where(Filter).GetEnumerator();
public new IEnumerator<T> GetEnumerator()
=> Filter is null
? base.GetEnumerator()
: this.Where(Filter).GetEnumerator();
private static readonly MethodInfo BaseIEnumerableGetEnumerator
= BaseType.GetMethod(
"System.Collections.IEnumerable.GetEnumerator",
BindingFlags.NonPublic | BindingFlags.Instance
)!;
BTW, my base class is List<T>. It also implements IList, ICollection, INotifyCollectionChanged and INotifyPropertyChanged.
Now - I set the filter.
Nothing happens.
So I call Refresh().
And to my surprise also nothing happens. Why? When Reset is sent to the ItemsCollection - the control should reload, and while reloading it should call GetEnumerator().
I set the breakpoint on my GetEnumerator() method, but it is not called on Refresh. WHY?
To clarify - I try to replicate exact ListCollectionView feature. It contains Refresh() method that applies the filtering.
Another weird thing I see is that my new GetEnumerator() is called by my own control, but it is not called AT ALL by DataGrid.
UPDATE:
As I've recently researched - built-in WPF controls might use some undocumented magic to bind items. They can trigger events on view model objects - that is possible (AFAIK) with Reflection.
IDK, using Reflection in the view you could dig into "underlying System Type" and use it's indexer if it's available to get items. In that case - it would just not use GetEnumerator.
I also checked the source code of ListCollectionView:
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/windows/Data/ListCollectionView.cs
It just uses a kind of shadow collection to achieve filtering. Well, that one way to achieve the effect for sure. But the easiest, if not the fastest way to filter any collection is to inject the filter into it's enumeration. No objects are created, no allocations, that should be fast. And easy. It works in my own control, that uses foreach on ItemsSource. It's obvious, foreach calls enumerator. There's no way around it. However, Microsoft's control obviously either don't use foreach or... they operate on something different than just the original items collection.
ItemsControl (including DataGrid, Listbox, etc.) works with the collection not directly, but through ICollectionView.
When a collection does not provide its own ICollectionView implementation (which is almost always the case), the ItemsControl itself creates the most suitable wrapper for it.
Typically, this is a CollectionView and types derived from it.
An example of a class that provides its own wrapper is CompositeCollection.
It provides a wrapper for the CompositeCollectionView.
The CollectionViewSource is also a means to create an ICollectionView for your collection.
Including using the GetDefaultView () method, you can return the default view of your collection.
This is what the ItemsControl uses when you pass your collection to it.
For almost all collections, a ListCollectionView will be a wrapper.
With the resulting wrapper, you can set properties to filter, group, sort, and render the collection view.
If you want to create your own presentation rules for your collection, then you need to implement the ICollectionViewFactory interface.
It has only one CreateView() method that returns the View wrapper for your collection.
You will have to create your own ICollectionView implementation (it's easier to do this based on the CollectionView or ListCollectionView classes).
And then return its instance in the CreateView() method.
OK, DataGrid just doesn't use GetEnumerator to display items.
It uses IList.this[index] and ICollection.Count for that.
So to implement filtering on my collection I had to create a ShadowList that contains filtered items.
Then I provided overrides for IList.this[index] and ICollection.Count to return items from the ShadowList.
Then I updated all adding to my collection to also update the ShadowList if the added item matches the Filter.
It works, however there's a catch: the filtered data is accessible only via IList indexer. So if the consuming control uses it - it will get filtered data, if not - it will get the original data.
I think it's preferable here. My view model needs original collection most of the time, and if it's not the case - I can always apply filter adding .Where(collection.Filter) to the enumerator.
GetEnumerator is called A LOT in a complex view model so it's best if it is the original List<T> enumerator.
The completed collection (ObservableList<T>) is available on GitHub:
https://github.com/HTD/Woof.Windows/blob/master/Woof.Windows.Mvvm/ObservableList.cs
It just uses a kind of shadow collection to achieve filtering.
For the WPF DataGrid, I believe it uses a backing CollectionViewSource, so filtering is a little bit different (as you say, it has a shadow collection).
MSDN has some info on filtering using the WPF DataGrid that I think might be helpful in your case.
I have a custom type that knows how to compare with others (it implements IComparable), it is working great stored inside an ObservableList.
However there is still somethings lacking in the collection. I am in need of 4 features. A collection that :
Detects duplicates and prevents insertions.
Automatically sorts on every successful insert.
Is observable &
If batches of items are inserted only notifies once the batch is
inserted.
I would like tips on how to make such a collection, what I will need to research/implement etc. I am not looking for code but if you can give that its a bonus.
What I have thought about doing :
Inheriting from ObservableCollection, overriding the add method, checking if the item already exists, if it does ignoring it.
Or
Implementing my own observable collection based off a more generic type like List.
Since you you want to prevent duplicate insertions and you want sorting, a SortedSet might be a good starting position. Since you want notifications, you'll have to extend the standard SortedSet and implement INotifyCollectionChanged and INotifyPropertyChanged. There's an example here and another one here which uses a HashSet instead (which is unordered, but you can easily replace with a SortedSet).
The alternative, which is just as valid, is to go with you first suggestion and extend ObservableCollection.
I know there are other posts similar to this one, but none of them answered my question fully.
I have multiple different observable collections in a WPF application that I am working on, and I need to just do a simple alphabetical sort on them to have them display in alphabetical order.
Is there an easy way to do this with Linq? If not, is there another easy way I can do this?
You can provide your own custom sorting logic or can use CollectionView sort descriptors property to sort your collection. For start look at these links, they might help -
http://bea.stollnitz.com/blog/?p=17
http://www.wpftutorial.net/DataViews.html
http://bea.stollnitz.com/blog/?p=24
What comes to mind:
Sort in View, not in ViewModel
Pull out into a list, sort list, clear collection, insert back
Custom SortedObservableCollection, but I am not sure how would View react to this.
Not sure about your specific needs.
You can use sorted observable collection - it is a collection which maintains the items in the sorted order at all times (so you just insert the item and it will get inserted into correct place into the collection).
There are several implementations out there - like this one or this one.
You should use CollectionViewSource instead of ObservableCollection or you can wrap your collection in CollectionViewSource.
public CollectionViewSource GetSortedView{
get{
CollectionViewSource view = new CollectionViewSource(myColl);
view.SortDescriptors.Add(new SortDescriptor("propertyname"), Ascending);
return view;
}
}
Benefit is, if you bind this view to datagrid etc, it will show the column sort sign and also let the user do further sorting.
Have a look at this one http://mokosh.co.uk/post/2009/08/04/how-to-sort-observablecollection/
Looks to be a clean and easy implementation.
And if you need to use just linq, you can use OrderBy extension method. More info on orderby method is here
Is there a simple way to have a bindinglist composed of several bindinglists? i.e. that is the "view" of the lists.
That is to say: I have 3 lists (list1,list2,list3). I want a list that is always the union of the 3 listx (we can suppose that no object is contained in 2 different lists).
Certainly, I can succeed in using the ListChange property but maybe there is a smarter way to do this?
To do this you would need to create your own type, implement IList, IBindingList (and ideally IBindingListView), and optionally ICancelAddNew and IRaiseItemChangedEvents. You'd also need either a public non-object indexer (public T this[int index] {get;}) or ITypedList.
From having done things similar to this, I strongly advise you; don't, unless this is really important. It would be more pragmatic to copy the references into a new BindingList<>.
Also; with new items; which list would it go into?
Have you looked into the CompositeCollection class?
Depending on what you're trying to do, it might help: its purpose is to combine multiple collections into a single collection (typically for display/binding purposes). So, you could create a CompositeCollection and add your three BindingList instances to it. The CompositeCollection will automatically update to include the members of the "child" lists.
I have a my custom collection derived from List(.MyItem.)
I want to trace if my collection is modified, how can I do that?
I am able to track Add Remove operations etc...by implement new definition of them but I don't see any method in List that gives me opportunity to track if collection is modified...
Take a look at a new type called ObservableCollection<T>. It has built in notification of changes, and it should be fairly simple to change your base class to this from list as both are very similar.