25

I have following code to re-order a ObservableCollection<T> collection:

list = new ObservableCollection<SomeType>( list.OrderBy( c=>c.Ordinal ) );

This code works, but I don't like the fact that "new" is involved. Is there a way I can change the internal element order of a ObservableCollection<T> collection without creating a new one?

Thanks,

sean717
  • 11,759
  • 20
  • 66
  • 90
  • A very convoluted approach is available [here](http://stackoverflow.com/questions/996126/sorting-an-observable-collection-with-linq). – Michael Todd Oct 19 '10 at 22:04

4 Answers4

14

Implement your custom sort extension method for Observable collection

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

Above answer is inspired by Mr. Jaider's reply to this question

Community
  • 1
  • 1
bkk
  • 523
  • 1
  • 5
  • 16
11

Given that OrderBy also news up an array to match the size of your collection, and several other objects, you've two choices:

  1. Give up on LINQ OrderBy altogether and write your own sort that performs in-place sorting over your ObservableCollection using the Move method.
  2. Wait until the current implementation becomes problematic then apply 1.

Don't worry, newing stuff up isn't so terrible. Linq does it all the time. Unless it's a bottleneck, all is good. Unless there's compelling evidence that sorting in-place will really speed up the show, then there's no problem here.

spender
  • 117,338
  • 33
  • 229
  • 351
  • Especially since the items themselves are not new but only the containing collection. Changing the collection object will also cause INPC bindings to refresh and show the new order. – Monstieur Jun 24 '13 at 10:25
  • 7
    The problem with doing this is that you'll loose any observer on the observable collection. – IEatBagels Oct 15 '15 at 14:21
  • 2
    There are some clients (ex.: Windows Store apps) that animates deleted or moved items in a ObservableCollection, using the events (created, moved, removed, etc.). Recreate the ObservableCollection is pointless! – JCKödel Jan 11 '17 at 17:59
9

My answer is inspired by bkk

 public static class ObservableCollection
    {
        public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector, bool isAZ)
        {
            if (isAZ)
            {
                List<TSource> sortedList = source.OrderBy(keySelector).ToList();
                source.Clear();
                foreach (var sortedItem in sortedList)
                {
                    source.Add(sortedItem);
                }
            }
            else
            {
                List<TSource> sortedList = source.OrderByDescending(keySelector).ToList();
                source.Clear();
                foreach (var sortedItem in sortedList)
                {
                    source.Add(sortedItem);
                }
            }         
        }
    }

Usage

 _someObservableCollection.Sort(x => x.Number, false); // Where number is an simple property (non-object)
z33k
  • 3,280
  • 6
  • 24
  • 38
NoWar
  • 36,338
  • 80
  • 323
  • 498
6

As far as I can tell, ObservableCollection has method for moving items inside the collection. You should get a comparer for your T-type, and then use some sorting algorithm

Onkelborg
  • 3,927
  • 1
  • 19
  • 22
  • This is the real answer right here. [ObservableCollection.Move(int,int)](https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1.move?view=net-5.0) can and should be used to implement your own sorting algorithm. Since my observable collections aren't too big, I use Selection Sort to minimize calls to Move, since every call invokes CollectionChanged. – Djordje Nikolic Sep 06 '21 at 18:36