Sorting an ObservableCollection<object> doesn't update the UI - c#

I'm building a Windows Phone 8.1 (WinRt) app.
I have an ObservableCollection<object>, i need to add, modify and sort the items of this collection.
when I add an item to this list everything is ok, if i cast one of this object in the list and i edit the property of the object the INotifyPropertyChanged takes care of updating the ui.
but when i sort the list the UI doesn't respect the order of the list.
the only way to update the UI is to use Move(), but as i have found this method is veri resource hungry.
I have tried with LINQ, but as a result the list is ordered, but the element in the UI remain in the same order.
there is any alternative way to sort this list?
this is some code insede my ViewModel
ActiveServices = ActiveServices.Where(x => x is ActiveServiceControlData).OrderByDescending(x => (x as ActiveServiceControlData).NotificationNumber).ToObservableCollection();
private static ObservableCollection<object> activeServices;
public ObservableCollection<object> ActiveServices
{
get { return activeServices; }
set
{
activeServices = value;
RaisePropertyChanged(() => ActiveServices);
}
}
EDIT
My big issue is that in the ObservableCollection there are different types of object, i use this collection as a ItemsSource for a ListView with an ItemTemplateSelector based on the type of the object inside the ObservableCollection, and i need to sort only the element of a specific type.

The proper way to sort ObservableCollection is to extend base ObservableCollection and make use of internal CollectionChanged events.
Your current code recreates whole collection which is inefficient (and your user interface may "blink").
public class SortableObservableCollection<T, TSortKey> : ObservableCollection<T>
{
private readonly Func<T, TKey> _sortByKey;
public SortableObservableCollection(Func<T, TKey> sortByKey)
{
_sortByKey = sortByKey;
}
public void Sort() {
// slow O(n^2) sort but should be good enough because user interface rarely has milion of items
var sortedList = Items.OrderBy(_sortByKey).ToList();
for (int i = 0; i < sortedList.Count; ++i)
{
var actualItemIndex = Items.IndexOf(sortedList[i]);
if (actualItemIndex != i)
Move(actualItemIndex, i);
}
}
}
.. and then just call .Sort();
The above method has big advantage over recreating whole item source - your user interface can react to that in pretty way (animation of item move instead of recreate "blink")

Related

How to cast list collection to derived collection object?

I have created a derived collection object to introduce some added functionality to filter the active records in the collection as shown in the below code snippet. How to achieve it as i want to just filter the same collection while keeping the original references in the filter without creating copy.
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes Active
{
get { return this.Where(x => x.IsActive).ToList(); } // Compile Error
}
}
Filtering an existing list
You mentioned that you wanted to just filter the existing list without keeping a copy. In this case, creating a List won't do, since creating a list from the subset will always create a new collection, not just a filter. List<T> is not a lazily-evaluated collection.
What you probably need to do is either define Active as IEnumerable<ExtendedType> and return the result of the Where directly (using LINQ's lazy implementation), or, if you're in WPF, use something like CollectionView as an additional filter on top of a collection, like this:
public ICollectionView ActiveTypes
{
get
{
if (_activeTypes == null)
{
_activeTypes = CollectionViewSource.GetDefaultView(myExtendedTypes);
_activeTypes.Filter = (type) => (type as ExtendedType).IsActive;
}
return _activeTypes;
}
}
You can now bind to ActiveTypes and get only a subset of the original list, filtered by the result of the Filter clause.
Creating a new List
However, assuming ExtendedType is a Reference type, you don't have to worry about copies of the items themselves being made by duplicating the list. If you don't mind creating a copy of the list with the same references, use my original answer:
The compiler is correct, in the sense that an ExtendedTypes is-a List<ExtendedType>, but not the other way around, and ToList() create a List<ExtendedType>.
There is, however, a simple workaround. Rather than ToList, just create a new ExtendedTypes with a constructor that initializes from a collection:
public class ExtendedTypes : List<ExtendedType>
{
public ExtendedTypes (IEnumerable<ExtendedType> items) : base(items)
{}
public ExtendedTypes Active
{
get { return new ExtendedTypes(this.Where(x => x.IsActive)); }
}
}

DataBinding reverses the order of my ObservableCollection?

I have the following custom observable collection (The code is taken in parts from Dean Chalk's blog http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx and slightly altered):
public class ThreadSaveObservableCollection <T> : IList<T>, INotifyCollectionChanged {
private IList<T> collection;
private Dispatcher uiDispatcher;
private ReaderWriterLock rwLock;
public ThreadSaveObservableCollection () {
collection = new List<T>();
rwLock = new ReaderWriterLock();
uiDispatcher = Dispatcher.CurrentDispatcher;
}
public void Insert (int index, T item) {
if (Thread.CurrentThread == uiDispatcher.Thread) {
insert_(index, item);
} else {
uiDispatcher.BeginInvoke(new Action<int, T>(insert_), DispatcherPriority.Normal, new object[] {index, item});
}
}
private void insert_ (int index, T item) {
rwLock.AcquireWriterLock(Timeout.Infinite);
collection.Insert(index, item);
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
rwLock.ReleaseWriterLock();
}
public IEnumerator<T> GetEnumerator () {
rwLock.AcquireReaderLock(Timeout.Infinite);
IEnumerator<T> enumerator = collection.GetEnumerator();
rwLock.ReleaseReaderLock();
return enumerator;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
rwLock.AcquireReaderLock(Timeout.Infinite);
IEnumerator<T> enumerator = collection.GetEnumerator();
rwLock.ReleaseReaderLock();
return enumerator;
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
... // the remaining methods of the IList<> interface
}
Further I have a ViewModel which holds an instance of this class:
public class ViewModel {
private ThreadSaveObservableCollection<string> Collection {get; set;}
public ViewModel () {
Collection = new ThreadSaveObservableCollection<string>();
}
public void Insert (string item) {
Collection.Insert(0, item);
}
}
I apply data binding in code-behind because I create the corresponding WPF control (an ordinary List control) with name "LogList" dynamically:
wpfContainer.LogList.ItemsSource = viewModel.Collection;
Everything works quite fine except the fact that the order of items in the wpf list control is reversed with respect to the items in the Collection object of the ViewModel.
With the statement Collection.Insert(0, intem) I expect to add the new item at the top of the list but what I get is the same result as I would use Collection.Add(item).
When I step into the code during runtime I can verify that the items inside my Collection are in the correct order but on the surface inside the wpf list control the order is altered i.e. reversed.
What am I making wrong ?
I guess the problem must be found somewhere around the data binding because it's the 'wire' that connects my ObservableCollection with the wpf control and it seems that a correct order is getting into the wire and an incorrect is leaving it.
Maybe it has something to do with the GetEnumerator() methods of the IList interface since the ItemSource property of the wpf control is awaiting an Enumerator ?
I have no clue and I am really stuck ...
Thank you in advance for any help ...
Can you try to do this:
http://msdn.microsoft.com/en-us/library/ms653208.aspx
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<object>() { item }, 0));
I think that this event is the problem.
Couple notes about your code:
Naming: ThreadSafe, not ThreadSave
Race conditions: you're acquiring a lock to call .GetEnumerator. Then releasing the lock, and returning that enumerator. That is not safe, and will throw an exception at runtime if the thread conditions are right. What you should do here is create a copy of the list while under lock, then return an enumerator to that copy.
ReaderWriterLock has some known performance, scalability, and error-prone usage (e.g. re-entrance) concerns. Use ReaderWriterLocksSlim instead.
The whole idea here is to marshal all operations to the UI thread. If everything happens on the UI thread, there's no need for any locking at all.
Finally, rather than re-invent the wheel, I suggest using one of the existing thread-safe ObservableCollections.

When binding an ObservableCollection<T> to a ListView, how does the ListView go about retrieving the data to display?

I was browsing around Microsoft's site, and got caught up reading ObservableCollection<T>.
I came up with a quick scenario in my head, and determined I could actually use something like this.
I made a sample class that inherets from ObservableCollection<T>, as seen here:
public class Ledger : ObservableCollection<LedgerEntry>, IEnumerable
{
private ObservableCollection<LedgerEntry> _items;
private decimal _currentBalance;
public Ledger(IEnumerable<LedgerEntry> items)
: base(items)
{
_items = new ObservableCollection<LedgerEntry>(items);
_currentBalance = 0m;
}
public new IEnumerator GetEnumerator()
{
var enumerator = _items.GetEnumerator();
while (enumerator.MoveNext())
{
var currentItem = enumerator.Current;
_currentBalance += currentItem.Amount;
currentItem.SetBalance(_currentBalance);
yield return currentItem;
}
}
}
Think about a bank account -- I'd like for each LedgerEntry contained within the collection to "know" it's balance.
Any who --
What I'm wondering is this -- given the above example, I see that GetEnumerator() is called once when I bind it to a ListView, and now I'm curious -- how is ListView actually retrieving / viewing the data contained within the collection?
My thought was that it would iterate the enumerable, but apparently I'm incorrect.
Any info on how this works?
Thanks!
If the collection implements IList<T> that will be used to access the items by index instead.

Implementing INotifyCollectionChanged on a collection without indexes

I am just now getting my toes wet in WPF after several years of working in ASP.Net exclusively. The problem I am currently struggling with is that I have a custom collection class which I need to bind to a listbox. Everything seems to be working except for removing an item from the collection. When I try to I get the error: “Collection Remove event must specify item position.” The catch is that this collection does not use indexes so I am not seeing a way to specify the position and so far Google has failed to show me a workable solution…
The class is defined to implement ICollection<> and INotifyCollectionChanged. My internal items container is a Dictionary which uses the item’s Name(string) value for a key. Aside from the methods defined by these two interfaces, this collection has an indexer that allows items to be accessed by Name, and overrides for the Contains and Remove methods so that they can also be called with the item Name. This is working for Adds and Edits but throws the above exception when I try to remove.
Here is an excerpt of the relevant code:
class Foo
{
public string Name
{
get;
set;
}
}
class FooCollection : ICollection<Foo>, INotifyCollectionChanged
{
Dictionary<string, Foo> Items;
public FooCollection()
{
Items = new Dictionary<string, Foo>();
}
#region ICollection<Foo> Members
//***REMOVED FOR BREVITY***
public bool Remove(Foo item)
{
return this.Remove(item.Name);
}
public bool Remove(string name)
{
bool Value = this.Contains(name);
if (Value)
{
NotifyCollectionChangedEventArgs E = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, Items[name]);
Value = Items.Remove(name);
if (Value)
{
RaiseCollectionChanged(E);
}
}
return Value;
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
#endregion
}
Your custom collection seems like a re-invention of KeyedCollection<TKey,TItem>, which internally uses a dictionary, and has indexes. The indexer for int indexes can get hidden if TKey is int or int-based enum, but this can be fixed.
As for making KeyedCollection work with WPF, I found this article, in which he basically makes an ObservableKeyedCollection<TKey,TItem> by implementing INotifyCollectionChanged and overriding SetItem(), InsertItem(), ClearItems(), and RemoveItem(), along with adding AddRange() and passing a Func<TItem,TKey> to the constructor for getting the TKey from a TItem.
Takes a little indirection, but you can do it with Linq. Not including error handling you can do this:
var items = dict.Keys.Select((k, i) => new { idx = i, key = k });
var index = items.FirstOrDefault(f => f.key == name).idx;
You could likewise use values instead of Keys, so long as you stay consistent.
So I threw in a temporary hack by changing the remove event to a reset, and went off to work on some other areas of my code. When I came back to this issue I discovered/realized that the SortedList class would satisfy my requirements and allow me to implement the Collection Changed events correctly with minimal changes to my existing code.
For those not familiar with this class (I had never used it before), here is a quick summary based on the reading I have done so far. In most ways it appears to behave like a dictionary, though the internal structure is different. This collection maintains sorted lists of the keys and values instead of a hash table. This means that there is a bit more overhead involved with getting data into and out of the collection but its memory consumption is lower. How noticeable this difference is appears to be dependent on how much data you need to store and what data types you are using for your keys.
Since my data volume in this instance is relatively low, and I need to have the items in the listbox sorted by their name values, using this class seems to be a good answer in my case. If anyone has an argument why this class should not be used, please let me know.
Thanks to all for their suggestions and comments, hopefully this thread helps someone else along the way.
I was able to use the NotifyCollectionChangedAction.Replace action with an empty NewItems list to raise the CollectionChanged event successfully for a non-indexed collection.

C# Property Access vs Interface Implementation

I'm writing a class to represent a Pivot Collection, the root object recognized by Pivot. A Collection has several attributes, a list of facet categories (each represented by a FacetCategory object) and a list of items (each represented by a PivotItem object). Therefore, an extremely simplified Collection reads:
public class PivotCollection
{
private List<FacetCategory> categories;
private List<PivotItem> items;
// other attributes
}
What I'm unsure of is how to properly grant access to those two lists. Because declaration order of both facet categories and items is visible to the user, I can't use sets, but the class also shouldn't allow duplicate categories or items. Furthermore, I'd like to make the Collection object as easy to use as possible. So my choices are:
Have PivotCollection implement IList<PivotItem> and have accessor methods for FacetCategory: In this case, one would add an item to Collection foo by writing foo.Add(bar). This works, but since a Collection is equally both kinds of list making it only pass as a list for one type (category or item) seems like a subpar solution.
Create nested wrapper classes for List (CategoryList and ItemList). This has the advantage of making a consistent interface but the downside is that these properties would no longer be able to serve as lists (because I need to override the non-virtual Add method I have to implement IList rather than subclass List. Implicit casting wouldn't work because that would return the Add method to its normal behavior.
Also, for reasons I can't figure out, IList is missing an AddRange method...
public class PivotCollection
{
private class CategoryList: IList<FacetCategory>
{
// ...
}
private readonly CategoryList categories = new CategoryList();
private readonly ItemList items = new ItemList();
public CategoryList FacetCategories
{
get { return categories; }
set { categories.Clear(); categories.AddRange(value); }
}
public ItemList Items
{
get { return items; }
set { items.Clear(); items.AddRange(value); }
}
}
Finally, the third option is to combine options one and two, so that PivotCollection implements IList<PivotItem> and has a property FacetCategories.
Question: Which of these three is most appropriate, and why?
The best thing to do here is to create your own collection class that inherits System.Collections.ObjectModel.Collection<T> and overrides InsertItem.

Categories