How to sort the observed collection?

I have the following class:

[DataContract] public class Pair<TKey, TValue> : INotifyPropertyChanged, IDisposable { public Pair(TKey key, TValue value) { Key = key; Value = value; } #region Properties [DataMember] public TKey Key { get { return m_key; } set { m_key = value; OnPropertyChanged("Key"); } } [DataMember] public TValue Value { get { return m_value; } set { m_value = value; OnPropertyChanged("Value"); } } #endregion #region Fields private TKey m_key; private TValue m_value; #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } #endregion #region IDisposable Members public void Dispose() { } #endregion } 

What I introduced in ObservableCollection:

 ObservableCollection<Pair<ushort, string>> my_collection = new ObservableCollection<Pair<ushort, string>>(); my_collection.Add(new Pair(7, "aaa")); my_collection.Add(new Pair(3, "xey")); my_collection.Add(new Pair(6, "fty")); 

Q: How to sort it by key?

+82
sorting c # wpf observablecollection
Dec 22 '09 at 10:21
source share
22 answers

OP Edit: many of them correctly pointed out that the original answer does not return the same collection (originally focused on sorting the vocabulary part of Q). See Editing below, where I refer to sorting an observable collection. The original stayed here, still getting voices

You can use linq, as the doSort method below shows. Quick code snippet: creates

3: Xey 6: FTY 7: aaa

Alternatively, you can use the extension method in the collection itself

 var sortedOC = _collection.OrderBy(i => i.Key); private void doSort() { ObservableCollection<Pair<ushort, string>> _collection = new ObservableCollection<Pair<ushort, string>>(); _collection.Add(new Pair<ushort,string>(7,"aaa")); _collection.Add(new Pair<ushort, string>(3, "xey")); _collection.Add(new Pair<ushort, string>(6, "fty")); var sortedOC = from item in _collection orderby item.Key select item; foreach (var i in sortedOC) { Debug.WriteLine(i); } } public class Pair<TKey, TValue> { private TKey _key; public TKey Key { get { return _key; } set { _key = value; } } private TValue _value; public TValue Value { get { return _value; } set { _value = value; } } public Pair(TKey key, TValue value) { _key = key; _value = value; } public override string ToString() { return this.Key + ":" + this.Value; } } 

EDIT

To return an ObservableCollection, call .ToObservableCollection on sortedOC, using, for example, this implementation .

OP EDIT Sorting the observed and returning the same sorted object can be done using the extension method. For large collections, keep track of how many collection notifications have changed, e.g.

 public static void Sort<T>(this ObservableCollection<T> observable) where T : IComparable<T>, IEquatable<T> { List<T> sorted = observable.OrderBy(x => x).ToList(); int ptr = 0; while (ptr < sorted.Count) { if (!observable[ptr].Equals(sorted[ptr])) { T t = observable[ptr]; observable.RemoveAt(ptr); observable.Insert(sorted.IndexOf(t), t); } else { ptr++; } } } 

usage: Sample with an observer (the Person class is used to make it simple)

 public class Person:IComparable<Person>,IEquatable<Person> { public string Name { get; set; } public int Age { get; set; } public int CompareTo(Person other) { if (this.Age == other.Age) return 0; return this.Age.CompareTo(other.Age); } public override string ToString() { return Name + " aged " + Age; } public bool Equals(Person other) { if (this.Name.Equals(other.Name) && this.Age.Equals(other.Age)) return true; return false; } } static void Main(string[] args) { Console.WriteLine("adding items..."); var observable = new ObservableCollection<Person>() { new Person { Name = "Katy", Age = 51 }, new Person { Name = "Jack", Age = 12 }, new Person { Name = "Bob", Age = 13 }, new Person { Name = "John", Age = 14 }, new Person { Name = "Mary", Age = 41 }, new Person { Name = "Jane", Age = 20 }, new Person { Name = "Jim", Age = 39 }, new Person { Name = "Sue", Age = 15 }, new Person { Name = "Kim", Age = 19 } }; //what do observers see? observable.CollectionChanged += (o, e) => { if (e.OldItems != null) { foreach (var item in e.OldItems) { Console.WriteLine("removed {0} at index {1}", item, e.OldStartingIndex); } } if (e.NewItems != null) { foreach (var item in e.NewItems) { Console.WriteLine("added {0} at index {1}", item, e.NewStartingIndex); } }}; Console.WriteLine("\nsorting items..."); observable.Sort(); }; 

Output above:
deleted Katie at the age of 51 on index 0
added Katie at the age of 51 on index 8
deleted Mary at the age of 41 on index 3
added by Mary at the age of 41 on index 7
removed Jane at the age of 20 on index 3
added by Jane at age 20 on index 5
deleted Jim at the age of 39 on index 3
added by jim at age 39 on index 6
removed Jane at the age of 20 on index 4
added by Jane at age 20 on index 5

The Person class implements both IComparable and IEquatable, the latter is used to minimize changes to the collection to reduce the number of change notifications raised

+18
Dec 22 '09 at 11:07
source share

This simple extension worked great for me. I just had to make sure MyObject was IComparable . When the sort method is called in the observed set of MyObjects , the CompareTo method on MyObject is called, which calls my Logical Sort method. Although he does not have all the bells and whistles of the remaining answers posted here, this is exactly what I need.

 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); } } public class MyObject: IComparable { public int CompareTo(object o) { MyObject a = this; MyObject b = (MyObject)o; return Utils.LogicalStringCompare(a.Title, b.Title); } public string Title; } . . . myCollection = new ObservableCollection<MyObject>(); //add stuff to collection myCollection.Sort(); 
+68
May 02 '13 at 18:13
source share

I know this question is old, but just happened while searching the Internet and found a corresponding blog entry that gives a better answer than the ones here:

http://kiwigis.blogspot.com/2010/03/how-to-sort-obversablecollection.html

UPDATE

The ObservableSortedList , which @romkyns points out in the comments, automatically maintains the sort order.

Implements an observable collection that stores its elements in sorted order. In particular, changes to the properties of an element are processed, which lead to changes in order.

However, note also the remark

It may be a mistake due to the comparative complexity of the interface and its relatively poor documentation (see https://stackoverflow.com/a/167185/ ).

+38
Mar 23 '11 at 6:12
source share

You can use this simple 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); } 

You can do the following:

 _collection.Sort(i => i.Key); 

More details: http://jaider.net/2011-05-04/sort-a-observablecollection/

+21
May 04 '11 at 21:35
source share

I liked the approach of the bubble expansion method on the Richie blog above, but I don't have to just sort the comparison of the whole object. Most often I want to sort by a specific property of an object. So I changed it to accept a key selector, like OrderBy does, so you can choose which property to sort:

  public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector) { if (source == null) return; Comparer<TKey> comparer = Comparer<TKey>.Default; for (int i = source.Count - 1; i >= 0; i--) { for (int j = 1; j <= i; j++) { TSource o1 = source[j - 1]; TSource o2 = source[j]; if (comparer.Compare(keySelector(o1), keySelector(o2)) > 0) { source.Remove(o1); source.Insert(j, o1); } } } } 

What would you call just like you would call OrderBy, except that it sorts the existing instance of your ObservableCollection instead of returning a new collection:

 ObservableCollection<Person> people = new ObservableCollection<Person>(); ... people.Sort(p => p.FirstName); 
+14
Jun 11 '12 at 18:02
source share

WPF provides live sorting out of the box using the ListCollectionView class ...

 public ObservableCollection<string> MyStrings { get; set; } private ListCollectionView _listCollectionView; private void InitializeCollection() { MyStrings = new ObservableCollection<string>(); _listCollectionView = CollectionViewSource.GetDefaultView(MyStrings) as ListCollectionView; if (_listCollectionView != null) { _listCollectionView.IsLiveSorting = true; _listCollectionView.CustomSort = new CaseInsensitiveComparer(CultureInfo.InvariantCulture); } } 

Once this initialization is complete, you have nothing more to do. The advantage over the passive type is that the ListCollectionView does all the heavy lifting in a way that is transparent to the developer. New items are automatically placed in their correct sort order. Any class derived from IComparer of T is suitable for a custom sort property.

See ListCollectionView for documentation and other functions.

+9
Jun 04 '16 at 19:36
source share

A variation is where you sort the collection in place using the sort sort algorithm. Elements are moved into place using the Move method. Each movement fires a CollectionChanged event with NotifyCollectionChangedAction.Move (as well as PropertyChanged with the property name Item[] ).

This algorithm has some good properties:

  • The algorithm can be implemented as a stable view.
  • The number of elements moved to the collection (e.g. CollectionChanged ) is almost always less than other similar algorithms such as insertion sort and bubble sort.

The algorithm is pretty simple. The collection is iterated to find the smallest item, which then moves to the beginning of the collection. The process is repeated starting from the second element and so on until all the elements are moved into place. The algorithm is not very efficient, but for everything that you are going to display in the user interface, it does not matter. However, in terms of the number of movement operations, it is quite effective.

Here is an extension method that requires elements to implement IComparable<T> for simplicity. Other options use IComparer<T> or Func<T, T, Int32> .

 public static class ObservableCollectionExtensions { public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable<T> { if (collection == null) throw new ArgumentNullException("collection"); for (var startIndex = 0; startIndex < collection.Count - 1; startIndex += 1) { var indexOfSmallestItem = startIndex; for (var i = startIndex + 1; i < collection.Count; i += 1) if (collection[i].CompareTo(collection[indexOfSmallestItem]) < 0) indexOfSmallestItem = i; if (indexOfSmallestItem != startIndex) collection.Move(indexOfSmallestItem, startIndex); } } } 

Sorting a collection is just a call to the extension method:

 var collection = new ObservableCollection<String>(...); collection.Sort(); 
+8
Jan 17 '13 at 2:26
source share

I would like to add NeilW to the answer . Include a method similar in order. Add this method as an extension:

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

And use like:

 myCollection = new ObservableCollection<MyObject>(); //Sorts in place, on a specific Func<T,T> myCollection.Sort(x => x.ID); 
+8
Mar 21 '15 at 9:02
source share

@NielW's answer is the way to actually sort in place. I wanted to add a slightly modified solution that circumvents the use of IComparable :

 static class Extensions { public static void Sort<TSource, TKey>(this ObservableCollection<TSource> collection, Func<TSource, TKey> keySelector) { List<TSource> sorted = collection.OrderBy(keySelector).ToList(); for (int i = 0; i < sorted.Count(); i++) collection.Move(collection.IndexOf(sorted[i]), i); } } 

now you can call it like any LINQ method:

 myObservableCollection.Sort(o => o.MyProperty); 
+7
Jul 27 '15 at 16:56
source share

To slightly improve the extension method on xr280xr, I added an optional bool parameter to determine if sorting is decreasing or not. I also included the suggestion made by Carlos P. in the commentary on this answer. See below.

 public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector, bool desc = false) { if (source == null) return; Comparer<TKey> comparer = Comparer<TKey>.Default; for (int i = source.Count - 1; i >= 0; i--) { for (int j = 1; j <= i; j++) { TSource o1 = source[j - 1]; TSource o2 = source[j]; int comparison = comparer.Compare(keySelector(o1), keySelector(o2)); if (desc && comparison < 0) source.Move(j, j - 1); else if (!desc && comparison > 0) source.Move(j - 1, j); } } } 
+4
Sep 22
source share

Do you need to constantly sort your collection? When retrieving pairs, do you need them to always be sorted, or is it only a few times (perhaps just for presentation)? How much do you expect from your collection? There are many factors that can help you choose how to use the witch.

If you need a collection that needs to be sorted at any time, even if you are inserting or deleting elements and the insertion speed is not a problem, perhaps you should implement some sort of SortedObservableCollection , for example, @Gerrie Schenck, mentioned or registered this implementation .

If you need your collection sorted just a few times, use:

 my_collection.OrderBy(p => p.Key); 

It will take some time to sort the collection, but even then it may be the best solution depending on what you do with it.

+2
Dec 22 '09 at 11:10
source share

My current answer already has the most votes, but I have found a better and more modern way to do this.

 class MyObject { public int id { get; set; } public string title { get; set; } } ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>(); //add stuff to collection // . // . // . myCollection = new ObservableCollection<MyObject>( myCollection.OrderBy(n => n.title, Comparer<string>.Create( (x, y) => (Utils.Utils.LogicalStringCompare(x, y))))); 
+2
Sep 07 '16 at 16:07
source share

Create a new SortedObservableCollection class, SortedObservableCollection it from the ObservableCollection and implement IComparable<Pair<ushort, string>> .

+1
Dec 22 '09 at 10:24
source share

One way would be to convert it to a list, and then call Sort (), providing a comparison delegate. Something like: -

(unverified)

 my_collection.ToList().Sort((left, right) => left == right ? 0 : (left > right ? -1 : 1)); 
+1
Dec 22 '09 at 11:13
source share
+1
Mar 26 '13 at 14:24
source share

What the hell I'll put in a quick-wrinkled answer too ... it looks a bit like some other implementations here, but I'll add it anywho:

(barely tested, hopefully I'm not embarrassed)

First, indicate some goals (my assumptions):

1) Must sort the ObservableCollection<T> in place, support notifications, etc.

2) Should not be terribly inefficient (i.e. something close to the standard "good" sorting efficiency)

 public static class Ext { public static void Sort<T>(this ObservableCollection<T> src) where T : IComparable<T> { // Some preliminary safety checks if(src == null) throw new ArgumentNullException("src"); if(!src.Any()) return; // N for the select, // + ~ N log N, assuming "smart" sort implementation on the OrderBy // Total: N log N + N (est) var indexedPairs = src .Select((item,i) => Tuple.Create(i, item)) .OrderBy(tup => tup.Item2); // N for another select var postIndexedPairs = indexedPairs .Select((item,i) => Tuple.Create(i, item.Item1, item.Item2)); // N for a loop over every element var pairEnum = postIndexedPairs.GetEnumerator(); pairEnum.MoveNext(); for(int idx = 0; idx < src.Count; idx++, pairEnum.MoveNext()) { src.RemoveAt(pairEnum.Current.Item1); src.Insert(idx, pairEnum.Current.Item3); } // (very roughly) Estimated Complexity: // N log N + N + N + N // == N log N + 3N } } 
+1
Mar 26 '13 at 15:39
source share

None of these answers worked in my case. Either because it drags on the binding, or it requires so much extra coding that it’s a kind of nightmare, or the answer is simply broken. So here is another simpler answer that I thought. This is much smaller than the code, and it remains the same observable collection with an additional type of method. Let me know if there is any reason why I should not do it this way (efficiency, etc.)?

 public class ScoutItems : ObservableCollection<ScoutItem> { public void Sort(SortDirection _sDir, string _sItem) { //TODO: Add logic to look at _sItem and decide what property to sort on IEnumerable<ScoutItem> si_enum = this.AsEnumerable(); if (_sDir == SortDirection.Ascending) { si_enum = si_enum.OrderBy(p => p.UPC).AsEnumerable(); } else { si_enum = si_enum.OrderByDescending(p => p.UPC).AsEnumerable(); } foreach (ScoutItem si in si_enum) { int _OldIndex = this.IndexOf(si); int _NewIndex = si_enum.ToList().IndexOf(si); this.MoveItem(_OldIndex, _NewIndex); } } } 

... where ScoutItem is my public class. It just seemed a lot easier. Added benefit: it really works and is not confused with bindings or returns a new collection, etc.

+1
Oct 12 '13 at
source share

Well, since I was having problems with the ObservableSortedList to work with XAML, I went and created a SortingObservableCollection . It inherits from ObservableCollection, so it works with XAML, and I tested it for 98% code coverage. I used it in my applications, but I will not promise that this is a mistake. Feel free to contribute. Here is an example of using the code:

 var collection = new SortingObservableCollection<MyViewModel, int>(Comparer<int>.Default, model => model.IntPropertyToSortOn); collection.Add(new MyViewModel(3)); collection.Add(new MyViewModel(1)); collection.Add(new MyViewModel(2)); // At this point, the order is 1, 2, 3 collection[0].IntPropertyToSortOn = 4; // As long as IntPropertyToSortOn uses INotifyPropertyChanged, this will cause the collection to resort correctly 

This is PCL, so it should work with Windows Store, Windows Phone and .NET 4.5.1.

+1
Jan 30 '14 at 17:58
source share

It worked for me, somewhere long ago it was.

 // SortableObservableCollection public class SortableObservableCollection<T> : ObservableCollection<T> { public SortableObservableCollection(List<T> list) : base(list) { } public SortableObservableCollection() { } public void Sort<TKey>(Func<T, TKey> keySelector, System.ComponentModel.ListSortDirection direction) { switch (direction) { case System.ComponentModel.ListSortDirection.Ascending: { ApplySort(Items.OrderBy(keySelector)); break; } case System.ComponentModel.ListSortDirection.Descending: { ApplySort(Items.OrderByDescending(keySelector)); break; } } } public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer) { ApplySort(Items.OrderBy(keySelector, comparer)); } private void ApplySort(IEnumerable<T> sortedItems) { var sortedItemsList = sortedItems.ToList(); foreach (var item in sortedItemsList) { Move(IndexOf(item), sortedItemsList.IndexOf(item)); } } } 

Using:

 MySortableCollection.Sort(x => x, System.ComponentModel.ListSortDirection.Ascending); 
+1
Oct 05 '15 at 13:03
source share

This is what I do with OC extensions:

  /// <summary> /// Synches the collection items to the target collection items. /// This does not observe sort order. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">The items.</param> /// <param name="updatedCollection">The updated collection.</param> public static void SynchCollection<T>(this IList<T> source, IEnumerable<T> updatedCollection) { // Evaluate if (updatedCollection == null) return; // Make a list var collectionArray = updatedCollection.ToArray(); // Remove items from FilteredViewItems not in list source.RemoveRange(source.Except(collectionArray)); // Add items not in FilteredViewItems that are in list source.AddRange(collectionArray.Except(source)); } /// <summary> /// Synches the collection items to the target collection items. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="source">The source.</param> /// <param name="updatedCollection">The updated collection.</param> /// <param name="canSort">if set to <c>true</c> [can sort].</param> public static void SynchCollection<T>(this ObservableCollection<T> source, IList<T> updatedCollection, bool canSort = false) { // Synch collection SynchCollection(source, updatedCollection.AsEnumerable()); // Sort collection if (!canSort) return; // Update indexes as needed for (var i = 0; i < updatedCollection.Count; i++) { // Index of new location var index = source.IndexOf(updatedCollection[i]); if (index == i) continue; // Move item to new index if it has changed. source.Move(index, i); } } 
+1
Dec 12 '15 at 18:08
source share

I needed to sort by several things, not one by one. This answer is based on some other answers, but it allows more complex sorting.

 static class Extensions { public static void Sort<T, TKey>(this ObservableCollection<T> collection, Func<ObservableCollection<T>, TKey> sort) { var sorted = (sort.Invoke(collection) as IOrderedEnumerable<T>).ToArray(); for (int i = 0; i < sorted.Count(); i++) collection.Move(collection.IndexOf(sorted[i]), i); } } 

When you use it, go to the OrderBy / ThenBy series of calls. Like this:

 Children.Sort(col => col.OrderByDescending(xx => xx.ItemType == "drive") .ThenByDescending(xx => xx.ItemType == "folder") .ThenBy(xx => xx.Path)); 
0
Nov 01 '16 at 14:52
source share
 var collection = new ObservableCollection<int>(); collection.Add(7); collection.Add(4); collection.Add(12); collection.Add(1); collection.Add(20); // ascending collection = new ObservableCollection<int>(collection.OrderBy(a => a)); // descending collection = new ObservableCollection<int>(collection.OrderByDescending(a => a)); 
-3
Mar 18 '13 at 20:03
source share



All Articles