2

I have Two separate observable Collection where T is a user defined class. These collections are binded to List View and Tree View. I want to show the items of the collections in sorted order. I don't seem to find any sort function on the List and Tree view. Elements in Collections can be removed/added on run time. What is the best way to achieve this?

Thanks in advance. Cheers!

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
WAQ
  • 2,556
  • 6
  • 45
  • 86
  • Don't know about treeview but this should work with ListView http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.aspx – paparazzo Oct 14 '13 at 16:27
  • this article will help you http://svetoslavsavov.blogspot.com/2009/09/sorting-and-filtering-databound.html – Aleksey Nov 12 '13 at 10:22

2 Answers2

5

You can implement this behaviour yourself quite easily using the internal Move method by extending the ObservableCollection<T> class. Here is a simplified example:

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    public SortableObservableCollection(IEnumerable<T> collection) : 
        base(collection) { }

    public SortableObservableCollection() : base() { }

    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        Sort(Items.OrderBy(keySelector));
    }

    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        Sort(Items.OrderBy(keySelector, comparer));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        Sort(Items.OrderByDescending(keySelector));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector, 
        IComparer<TKey> comparer)
    {
        Sort(Items.OrderByDescending(keySelector, comparer));
    }

    public void Sort(IEnumerable<T> sortedItems)
    {
        List<T> sortedItemsList = sortedItems.ToList();
        for (int i = 0; i < sortedItemsList.Count; i++)
        {
            Items[i] = sortedItemsList[i];
        }
    }
}

Thanks to @ThomasLevesque for the more efficient Sort method shown above

You can then use it like this:

YourCollection.Sort(c => c.PropertyToSortBy);
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Your solution is correct but my ObserveCollections are filled with methods that already return me ObservabeCollection. I cant seem to cast them to SortedOvservableCollection. SO this solution does not seem to work. – WAQ Oct 14 '13 at 15:57
  • I don't really understand what is stopping you from using this class... just add your existing methods to this class and return `SortableObservableCollection` from them instead. Alternatively, you can just the `Sort` method from the example anywhere you like. You asked for the best method of sorting these collections and this is it... just adapt your code to use it. – Sheridan Oct 14 '13 at 16:05
  • 1
    Also, please don't say that this solution does not work, just because you are having some difficulty applying it to your code. You are misleading other users that may be searching for the same functionality. – Sheridan Oct 14 '13 at 16:14
  • 2
    This solution is *very* inefficient... its complexity is at least O(n²). Most sort algorithms have a O(n log n) complexity. – Thomas Levesque Oct 14 '13 at 16:30
  • @ThomasLevesque, I don't deny that it *could* be implemented more efficiently... please feel free to show us a better method. – Sheridan Oct 14 '13 at 18:12
  • @Sheridan, I just changed the Sort method a little to avoid the costly IndexOf calls: https://gist.github.com/thomaslevesque/6d1e2c90806ff5b60782#file-gistfile1-cs. Of course it's still far from optimal... – Thomas Levesque Oct 14 '13 at 19:36
  • @ThomasLevesque, thanks for that. I've edited my answer with your more efficient example... I can't believe that I didn't think of that myself. – Sheridan Oct 14 '13 at 20:17
  • @ThomasLevesque, interestingly after I updated my `Sort` method, users of my application started to complain that the re-ordering of data objects was no longer working... when I looked into it, it turned out that the collections were still getting re-ordered and sorted, but now using your `Sort` method, the UI was no longer automatically being updated. When I changed the code back to my original code, it fixed the problem and everything worked properly again. I'm guessing that this has something to do with the `Move` method, but I don't know why. I knew there was a reason for using that code. – Sheridan Oct 16 '13 at 12:20
  • 1
    That's because accessing `Items` directly doesn't trigger the CollectionChanged event. It can be fixed easily: just replace `Items[i]` with `this[i]`. Another option is to trigger a CollectionChanged event with Action = Reset at the end of the sort. This way, you trigger only one event and the UI only needs to refresh once. – Thomas Levesque Oct 16 '13 at 12:38
  • Thanks for getting back to me @ThomasLevesque... I was just about to ask a new question here. Now I understand the situation better, but unfortunately only my original method works as I want it to. This is because my data objects are all animated as they move positions and when using your example of using `this` instead of the `Items` property, the animation disappears. When using the `NotifyCollectionChangedAction.Reset` enumeration, they all move as if they were being re-added all together, or as if calling the `PropertyChanged` event on the collection property. Many thanks all the same. – Sheridan Oct 16 '13 at 13:01
  • 1
    @Aleksey, really??? How immature are you? I down voted you on the [Binding to a method in DataContext](http://stackoverflow.com/questions/19924376/binding-to-a-method-in-datacontext/19924844#19924844) post because you provided an absolutely incorrect answer. So your response was to find any one of my answers just so that you could down vote me in retaliation?? That is so pathetic of you... totally unbelievable that a StackOverflow user would behave in this way... very disappointing. – Sheridan Nov 12 '13 at 10:30
  • Well @Aleksey, thank you at least for admitting that. If you remove the down vote, we can move past this issue and hopefully say that lessons have been learned. – Sheridan Nov 12 '13 at 10:51
  • 1
    @Aleksey, the reputation points do not matter to me and as you seem to have changed your attitude since down voting, I accept your earlier apology. Please think twice before down voting in the future... there are specific conditions under which they are supposed to be used. I do accept that I could have left a better comment when I down voted your answer, so I hope we have both learned something from this. – Sheridan Nov 12 '13 at 11:02
0
  private void ApplySort(IEnumerable<T> sortedItems)
  {
     var sortedItemsList = sortedItems.ToList();
     for (int i = 0; i < sortedItemsList.Count; i++)
     {
        if((object)(this[i]) != (object)(sortedItemsList[i]))
           this[i] = sortedItemsList[i];
     }
  }

Can reduce the number of CollectionChanged events for better performance.

Neil
  • 1