none
Sorting an ObservableCollection<T>

    Question

  • I was recently using a List<T> to keep track of my data, but we had to move to an ObservableCollection<T> because it will eventually be used in binding to a UI.  I was using the List<T>.Sort(IComparer<T>) to sort the list, but it appears that ObservableCollection<T> does not implement this.  Is there a way to sort it or do I have to do that manually? 

    It seems odd to have to redo this work that clearly is already implemented in .Net but I can't seem to find a way to do it. I figured it was worth asking about.  Thanks for the help.
    Monday, January 08, 2007 10:11 PM

Answers

  • You can sort it on UI level thru CollectionViewSoure. (http://msdn2.microsoft.com/en-us/library/ms742542.aspx)

    Monday, January 08, 2007 11:18 PM
  • You can write your own sorting logic, something like that:

    public class SortableObservableCollection<T> : ObservableCollection<T>

    {

    public void Sort()

    {

    Sort(Comparer<T>.Default);

    }

    public void Sort(IComparer<T> comparer)

    {

    int i, j;

    T index;

    for (i = 1; i < Count; i++)

    {

    index = thisIdea;     //If you can't read it, it should be index = this[x], where x is i :-)

    j = i;

    while ((j > 0) && (comparer.Compare(this[j - 1], index) == 1))

    {

    this[j] = this[j - 1];

    j = j - 1;

    }

    this[j] = index;

    }

    }

    }

    If you planning to sort collections with 5000 items or more it's better to use another algorithm, I can write you a code if you want, let me know.

     

    Tuesday, January 09, 2007 1:03 AM

All replies

  • You can sort it on UI level thru CollectionViewSoure. (http://msdn2.microsoft.com/en-us/library/ms742542.aspx)

    Monday, January 08, 2007 11:18 PM
  • Thanks for the idea.  I saw something about that before but was hoping to do the sorting on the business logic instead of at the UI so if another developer could reuse it without worrying about sorting it again

    If there is anyone knows a way of sorting an ObservableCollection directly I would appreciate it.  Our current communications layer only supports ObservableCollection and not Lists anymore so I'm sure I'll want to sort an observable collection that might not be displayed.

    Thanks again.
    Monday, January 08, 2007 11:57 PM
  • You can write your own sorting logic, something like that:

    public class SortableObservableCollection<T> : ObservableCollection<T>

    {

    public void Sort()

    {

    Sort(Comparer<T>.Default);

    }

    public void Sort(IComparer<T> comparer)

    {

    int i, j;

    T index;

    for (i = 1; i < Count; i++)

    {

    index = thisIdea;     //If you can't read it, it should be index = this[x], where x is i :-)

    j = i;

    while ((j > 0) && (comparer.Compare(this[j - 1], index) == 1))

    {

    this[j] = this[j - 1];

    j = j - 1;

    }

    this[j] = index;

    }

    }

    }

    If you planning to sort collections with 5000 items or more it's better to use another algorithm, I can write you a code if you want, let me know.

     

    Tuesday, January 09, 2007 1:03 AM
  • Thanks for the reply.  I guess this is the best option if you want to have the observable collection sort itself.  I'll look into both suggestions and see what works best in my case.

    Thanks again for both replies!
    Tuesday, January 09, 2007 4:28 PM
  • Have anyone a performed sorting algorithm for the ObservableCollection<T>

    Thanks!

    Tuesday, July 31, 2007 1:52 PM
  • but after you sort the ObservableCollection<T> in the background, how do you get it to update the UI?

    Thanks.
    • Proposed as answer by Charley Chen Sunday, April 04, 2010 7:59 AM
    Tuesday, July 08, 2008 2:19 PM
  • Hi all,

    I realise that this is an old thread, but I recently ran into the same issue.

    I have an ObservableCollection<T> set as the ItemsSource of an ItemsCollection. I'm using an animated wrap panel to animate the generated UI into position. Later, I want to reorder the items in the ObservableCollection in place so that the animated wrap panel animates them into their new positions.

    To do this, I needed a sorting function that uses the Move method of the ObservableCollection (this is because removing an item and adding it again causes new UI to be generated rather than the existing UI to be animated to its new position).

    This turned out (eventually) to be very simple by subclassing ObservableCollection<T> as follows:

    /// <summary>
    /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed and allows sorting.
    /// </summary>
    /// <typeparam name="T">The type of elements in the collection.</typeparam>
    public class SortableObservableCollection<T> : ObservableCollection<T>
    {
        /// <summary>
        /// Sorts the items of the collection in ascending order according to a key.
        /// </summary>
        /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
        /// <param name="keySelector">A function to extract a key from an item.</param>
        public void Sort<TKey>(Func<T, TKey> keySelector)
        {
            InternalSort(Items.OrderBy(keySelector));
        }
    
        /// <summary>
        /// Sorts the items of the collection in ascending order according to a key.
        /// </summary>
        /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
        /// <param name="keySelector">A function to extract a key from an item.</param>
        /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
        public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
        {
            InternalSort(Items.OrderBy(keySelector, comparer));
        }
    
        /// <summary>
        /// Moves the items of the collection so that their orders are the same as those of the items provided.
        /// </summary>
        /// <param name="sortedItems">An <see cref="IEnumerable{T}"/> to provide item orders.</param>
        private void InternalSort(IEnumerable<T> sortedItems)
        {
            var sortedItemsList = sortedItems.ToList();
    
            foreach (var item in sortedItemsList)
            {
                Move(IndexOf(item), sortedItemsList.IndexOf(item));
            }
        }
    }



    The ItemsSource is then sorted as follows (using a lambda expression that converts a T (in this case a Player) into an int key):

    ((SortableObservableCollection<Player>)OrderDisplay.ItemsSource).Sort(player => GetOrder(player).Value);



    This way, the internal OnCollectionChanged is called with a NotifyCollectionChangedEventArgs Action of Move, which allows the ItemsControl to re-layout the existing UI (which is then animated by the animated wrap panel) instead of generating new UI.

    • Proposed as answer by str8asacircle Wednesday, April 15, 2009 3:14 AM
    Wednesday, April 15, 2009 3:14 AM
  • Hi str8asacircle,

    First, thanks for your contribution.

    I am getting a compile error when using your SortableObservableCollection. The Move method is not recognized by the compiler. What is it I am missing? Thanks in advance.

    Thursday, April 23, 2009 1:43 PM
  • Hi Michel,

    Move is a method on the base class (ObservableCollection(T).Move - see http://msdn.microsoft.com/en-us/library/ms654933.aspx), so I'm not sure why the compiler isn't seeing it...

    Which version of the Framework are you targeting?

    Cheers,
    Matt.
    Thursday, April 23, 2009 4:12 PM
  • Hi Matt,

    My fault. I am developing a Silverlight client. Silverlight supports only a subset of the .NET library. The Move method isn't part of the ObservableCollection class.

    Thanks for your response.
    Michel
    Thursday, April 23, 2009 6:04 PM
  • You can use Lambda Expression:

    List<CSomeClass> temp = observableCollectionObject.ToList();
    temp.Sort((x, y) => string.Compare(x.Title, y.Title));
    ObservableCollection<CSomeClass> result = new ObservableCollection<CSomeClass>(temp);
    Thursday, September 10, 2009 10:15 AM
  • You can use Lambda Expression:

    List<CSomeClass> temp = observableCollectionObject.ToList();
    
    temp.Sort((x, y) => string.Compare(x.Title, y.Title));
    
    ObservableCollection<CSomeClass> result = new ObservableCollection<CSomeClass>(temp);

    I don't know where you took this but there's no constructor on the ObservableCollection class
    am I wrong ?
    Saturday, October 10, 2009 10:59 AM
  • Have a look at http://softcollections.codeplex.com/ . You can find there implementation of the SortedObservableCollection.
    Friday, November 13, 2009 10:48 AM
  • This Sortable... class is perfect, thanks.


    “Somewhere someone is practicing, if you're not and you should meet, you will lose…”
    Tuesday, December 15, 2009 10:06 PM
  • For those who are interested, here is a solution to the problem I came up with using an extension method.

     

     

    	static class Extensions
    	{
    		public static void Sort<T>( this ObservableCollection<T> collection)  where T : IComparable
    		{
    			List<T> sorted = collection.OrderBy(x => x).ToList();
    			for (int i = 0; i < sorted.Count(); i++)
    				collection.Move(collection.IndexOf(sorted[i]), i);
    		}
    	}

    This solution requires that the class being stored in the ObservableCollection implement IComparable.  The nice thing about this solution is it shows up as a method on the appropriate Observable Collections.

    ex.

    // KeyField implements IComparable
    public ObservableCollection<KeyField> KeyFields { get; set; }
    
    // Sort the collection in place
    KeyFields.Sort();

    Thursday, March 18, 2010 5:56 PM
  • This my method of sorting

     

     I can use any property of object <T> for sorting using LINQ

        Public Sub SortByPhoneNumber(ByVal Ascending As Boolean)
            Dim Sorted As IOrderedEnumerable(Of <T>)
            If Ascending Then
                Sorted = From oP As oPoint In Me.Items Select oP Order By <T>.<Property Name> Ascending
            Else
                Sorted = From oP As oPoint In Me.Items Select oP Order By <T>.<Property Name> Descending
            End If
    
            Dim NewIndex As Integer = 0
            For Each P As <T> In Sorted
                Dim Idx As Integer = Me.IndexOf(P)
                Me.Move(Idx, NewIndex)
                NewIndex += 1
            Next
        End Sub
    

    Sunday, March 28, 2010 3:58 PM
  • This my method of sorting

     

     I can use any property of object <T> for sorting using LINQ

        Public Sub SortByPhoneNumber(ByVal Ascending As Boolean)
            Dim Sorted As IOrderedEnumerable(Of <T>)
            If Ascending Then
                Sorted = From oP As <T> In Me.Items Select oP Order By <T>.<Property Name> Ascending
            Else
                Sorted = From oP As <T> In Me.Items Select oP Order By <T>.<Property Name> Descending
            End If
    
            Dim NewIndex As Integer = 0
            For Each P As <T> In Sorted
                Dim Idx As Integer = Me.IndexOf(P)
                Me.Move(Idx, NewIndex)
                NewIndex += 1
            Next
        End Sub
    
    

     


    Sunday, March 28, 2010 4:00 PM
  • Here's another solution:

     public static void Sort<T>(this ObservableCollection<T> collection, IComparable<T> comparable)
     {
        Array.Sort<T>(collection, comparable);
     }

    -Alex

     

     

     

    Saturday, April 24, 2010 9:05 PM
  • Here's an extension method based on Rbdavidson13's suggestion that you can use that doesn't require IComparable<T> on the object in the collection:

     

    public static class Extensions
    {
    	public static void Sort<T>(this ObservableCollection<T> collection, Comparison<T> comparison)
    	{
    	  var comparer = new Comparer<T>(comparison);
    	  
    	  List<T> sorted = collection.OrderBy(x => x, comparer).ToList();
    
    	  for (int i = 0; i < sorted.Count(); i++)
    		collection.Move(collection.IndexOf(sorted[i]), i);
    	}
    
    	private class Comparer<T> : IComparer<T>
    	{
    	  private readonly Comparison<T> comparison;
    
    	  public Comparer(Comparison<T> comparison)
    	  {
    		this.comparison = comparison;
    	  }
    
    	  #region IComparer<T> Members
    
    	  public int Compare(T x, T y)
    	  {
    		return comparison.Invoke(x, y);
    	  }
    
    	  #endregion
    	}
    }

     

    Here, you can just use a Comparison<T> lambda instead like:

     

    collection.Sort((x, y) => x.Name.CompareTo(y.Name));

     

    Thursday, June 03, 2010 4:20 PM
  • I think a nice and simple solution provided by makosh which works perfect@: 

    http://mokosh.co.uk/post/2009/08/04/how-to-sort-observablecollection/

     

    Regards

    Nishant rana

    • Proposed as answer by nishantcop Friday, December 03, 2010 10:09 AM
    Friday, August 27, 2010 2:45 AM
  • This works with little observableCollection (150).

    But when you have a very big Collection !!!!!!

    Would you please propose another  algorithm for this type of Collection (5000)

    Monday, November 08, 2010 3:14 PM
  • Thanks Jason! This extension class works like a charm.

    Tuesday, February 22, 2011 8:51 AM
  • I'm binding a list of ints to a listbox. Here is how I sorted the list. It's simple and so far it works.

    private

     

    static ObservableCollection<Int32> runBooks = new ObservableCollection<Int32>();

    public ObservableCollection<Int32>

    RunBooks

    {

     

    get { return

    runBooks; }

     

    set

    {

     

    if (value !=

    runBooks)

    {

     

    List<int> temp = value.

    ToList();

    temp

    .

    Sort();

    runBooks

    = new ObservableCollection<Int32>

    (temp);

    OnPropertyChanged(

    new PropertyChangedEventArgs("RunBooks"

    ));

    }

    }

    }

    Sunday, April 03, 2011 2:37 AM
  • Hello !

    I'm having some trouble with ObservableCollection - sorting. Suppose that my items are added in time in this list and i need to mentain it sorted. I see the answers from this page, but the problem is that my list might be very big. So, is there any method to have a logN complexity when inserting a new element ? Or there is other form of observable collection that uses sets, skip lists, etc ?

    Friday, April 22, 2011 3:30 PM
  • The best way would be to be able to specify a Capacity for the ObservableCollection (1). Because that's not implmented yet the best option is probably to implement your own class from the interface and add the Capacity paradigm by yourself. Although sorting will definetely make the operation not logN but should still improve performance. Having sorted lists is never really performance-friendly unless you have some pretty creative implementations.

     

    (1) http://connect.microsoft.com/VisualStudio/feedback/details/380979/add-an-observablecollection-t-int-capacity-constructor-for-specifying-initial-capacity


    WPF & .NET Tutorials
    Friday, April 22, 2011 4:27 PM
  • You will find the solution here: http://jaider.net/2011-05-04/sort-a-observablecollection/

    Using this sort method:

    public static void Sort<TSource, TKey>(this Collection<TSource> source, Func<TSource, TKey> keySelector)
    {
      List<TSource> sortedList = source.OrderBy(keySelector).ToList();
      source.Clear();
      foreach (var sortedItem in sortedList)
        source.Add(sortedItem);
    }
    

    Wednesday, May 04, 2011 9:52 PM
  • @Jaider Ariza Coba: That one really worked, even in a ObservableCollection bound to the ItemsSource of a ListBox.

     

    Nice, thanks!


    Saturday, May 07, 2011 10:02 AM
  • Thanks for ur solution.

    it's really helpful.

    Monday, June 13, 2011 10:35 AM
  • I realize that this is is a really old thread, but it strikes me that most if these solutions are overly complicated.  I don't bother trying to sort the ObservableCollection in place.  I just replace it with a sorted one.  Let's assume that I have a service method that gets an ObservableCollection of items.  (It could be a List or array or any type of collection as well, but I just configure my data service to return ObservableCollections because most of the time I bind to them anyway and it saves me a step.)  Let's also say that I want to sort that collection of items by the item description.  I would do this:

    ObservableCollection<T> _items = proxy.ServiceMethodThatGetsItems();
    this.Items = new ObservableCollection<T>(_items.OrderBy(i => i.DESCRIPTION));
    
    
    

    If I want to sort the list, rather than use the ObservableCollection or List as it was returned from the data service, I sort it using LinQ's OrderBy() extension method and use that to create a new ObservableCollection which I then assign to the property I want to bind to.

    The reason this works is that ObservableCollections and Lists have a constructor that will accept an IEnumerable as an argument.  When you use the OrderBy() method to sort a list/collection/array/etc., it returns an IEnumerable.  So if you just create a new ObservableCollection and pass in the result of the the OrderBy method, you get a new, sorted ObservableCollection with the items you want.

    As I said, it doesn't really matter how the data is returned from your data service.  This would work just as well:

    List<T> _items = proxy.ServiceMethodThatGetsItems();
    this.Items = new ObservableCollection<T>(_items.OrderBy(i => i.DESCRIPTION));
    
    
    

    as would:

    T[] _items = proxy.ServiceMethodThatGetsItems();
    this.Items = new ObservableCollection<T>(_items.OrderBy(i => i.DESCRIPTION));
    
    
    

    I hope this helps.


    JohnnyG
    Thursday, January 19, 2012 4:45 PM
  • Johnny, if the list you want to sort is already bound to an UI List,  recreating a new ObservableColletion will break the binding and the changes won't be reflected in the UI. You need to ensure you keep the same collection instance.

    My solution would be to create a new Class, derive it from ObservableCollection and add a method AddRange(IEnumerable). Just make sure that when you call the AddRange method, you disable the ObservableCollection's notifications and just trigger it at the end with all the modifications at once. I can provide the actual implementation if needed..

    Thursday, February 09, 2012 6:31 PM
  • can I use this Sort method to sort my collection based on a string property which is in a collection iteself. what parameter to pass to it. {e.g. something like
    ASPTrunks.Sort(c = c.SpTypeName);

    }

    Dheeraj




    Dheeraj

    Tuesday, October 23, 2012 12:31 PM