Comment sortinger une collection observable?

J’ai un cours suivant:

[DataContract] public class Pair : 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(ssortingng name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } #endregion #region IDisposable Members public void Dispose() { } #endregion } 

Ce que j’ai mis dans un ObservableCollection:

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

Q: Comment puis-je le sortinger par clé?

OP Edit: comme beaucoup l’ont correctement fait remarquer, la réponse d’origine ne renvoie pas la même collection, (à l’origine plus centrée sur le sorting de la partie dictionnaire du Q). S’il vous plaît voir éditer en bas où j’adresse le sorting d’une collection observable. Original laissé ici comme recevant encore des votes

Vous pouvez utiliser linq comme le montre la méthode doSort ci-dessous. Un extrait de code rapide: produit

3: xey 6: fty 7: aaa

Sinon, vous pouvez utiliser une méthode d’extension sur la collection elle-même

 var sortedOC = _collection.OrderBy(i => i.Key); private void doSort() { ObservableCollection> _collection = new ObservableCollection>(); _collection.Add(new Pair(7,"aaa")); _collection.Add(new Pair(3, "xey")); _collection.Add(new Pair(6, "fty")); var sortedOC = from item in _collection orderby item.Key select item; foreach (var i in sortedOC) { Debug.WriteLine(i); } } public class Pair { 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 ssortingng ToSsortingng() { return this.Key + ":" + this.Value; } } 

MODIFIER

Pour renvoyer un object ObservableCollection, appelez .ToObservableCollection sur sortOC en utilisant, par exemple, cette implémentation .

OP EDIT Il est possible de sortinger une observable et de renvoyer le même object sortingé en utilisant une méthode d’extension. Pour les plus grandes collections, faites attention au nombre de notifications de collecte modifiées, par exemple

 public static void Sort(this ObservableCollection observable) where T : IComparable, IEquatable { List 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: Echantillon avec un observateur (utilise une classe de personne pour restr simple)

 public class Person:IComparable,IEquatable { public ssortingng 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 ssortingng ToSsortingng() { 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(ssortingng[] args) { Console.WriteLine("adding items..."); var observable = new ObservableCollection() { 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(); }; 

Sortie ci-dessus:
Katy âgée de 51 ans à l'indice 0
Katy a 51 ans à l'index 8
Mary a 41 ans à l'index 3
a ajouté Mary âgée de 41 ans à l'indice 7
enlevé Jane âgée de 20 ans à l'index 3
a ajouté Jane âgée de 20 ans à l'indice 5
enlevé Jim âgé de 39 ans à l'index 3
a ajouté Jim âgé de 39 ans à l'indice 6
enlevé Jane âgée de 20 ans à l'index 4
a ajouté Jane âgée de 20 ans à l'indice 5

La classe Person implémente à la fois IComparable et IEquatable, ce dernier est utilisé pour minimiser les modifications apscopes à la collection afin de réduire le nombre de notifications de modification émises.

Cette simple extension a fonctionné à merveille pour moi. Je devais juste m’assurer que MyObject était IComparable . Lorsque la méthode de sorting est appelée sur la collection observable de MyObjects , la méthode MyObject sur MyObject est appelée, ce qui appelle ma méthode de sorting logique. Bien que toutes les réponses ne soient pas affichées ici, c’est exactement ce dont j’avais besoin.

 static class Extensions { public static void Sort(this ObservableCollection collection) where T : IComparable { List 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(); //add stuff to collection myCollection.Sort(); 

Je sais que cette question est ancienne, mais je l’ai juste rencontrée en faisant une recherche sur Google et j’ai trouvé une entrée de blog pertinente qui fournit une meilleure réponse que celles ici:

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

METTRE À JOUR

La classe ObservableSortedList que @romkyns indique dans les commentaires conserve automatiquement l’ordre de sorting.

Implémente une collection observable qui conserve ses éléments dans l’ordre sortingé. En particulier, les modifications apscopes aux propriétés d’un élément entraînant des modifications de l’ordre sont traitées correctement.

Cependant notez aussi la remarque

Peut-être en raison de la complexité relative de l’interface et de sa documentation relativement médiocre (voir https://stackoverflow.com/a/5883947/33080 ).

Vous pouvez utiliser cette méthode simple:

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

Vous pouvez sortinger comme ceci:

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

Plus de détails: http://jaider.net/2011-05-04/sort-a-observablecollection/

J’ai aimé l’approche de la méthode d’extension de sorting à bulles sur le blog de “Richie” ci-dessus, mais je ne veux pas forcément sortinger simplement la comparaison de l’object entier. Je veux plus souvent sortinger sur une propriété spécifique de l’object. Je l’ai donc modifié pour accepter un sélecteur à clé comme le fait OrderBy pour pouvoir choisir la propriété à sortinger:

  public static void Sort(this ObservableCollection source, Func keySelector) { if (source == null) return; Comparer comparer = Comparer.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); } } } } 

Ce que vous appelez de la même manière que vous appeleriez OrderBy, sauf que cela va sortinger l’instance existante de votre ObservableCollection au lieu de renvoyer une nouvelle collection:

 ObservableCollection people = new ObservableCollection(); ... people.Sort(p => p.FirstName); 

WPF fournit un sorting en direct en utilisant la classe ListCollectionView

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

Une fois cette initialisation terminée, il n’y a plus rien à faire. L’avantage par rapport à un sorting passif est que ListCollectionView se charge de tout, d’une manière transparente pour le développeur. Les nouveaux articles sont automatiquement placés dans leur ordre de sorting correct. Toute classe IComparer d’ IComparer of T convient à la propriété de sorting personnalisée.

Voir ListCollectionView pour la documentation et les autres fonctionnalités.

J’aimerais append à la réponse de NeilW . Pour incorporer une méthode qui ressemble au orderby. Ajoutez cette méthode en tant qu’extension:

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

Et utiliser comme:

 myCollection = new ObservableCollection(); //Sorts in place, on a specific Func myCollection.Sort(x => x.ID); 

La réponse de @ NielW est la voie à suivre pour un sorting réel sur place. Je voulais append une solution légèrement modifiée qui vous permet de ne pas avoir à utiliser IComparable :

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

maintenant vous pouvez l'appeler comme la plupart des méthodes LINQ:

 myObservableCollection.Sort(o => o.MyProperty); 

Une variante consiste à sortinger la collection en place à l’aide d’un algorithme de sorting de sélection . Les éléments sont mis en place à l’aide de la méthode Move . Chaque déplacement déclenche l’événement CollectionChanged avec NotifyCollectionChangedAction.Move (et également PropertyChanged avec le nom de propriété Item[] ).

Cet algorithme a quelques propriétés intéressantes:

  • L’algorithme peut être implémenté comme un sorting stable.
  • Le nombre d’éléments déplacés dans la collection (par exemple, les événements CollectionChanged déclenchés) est presque toujours inférieur aux autres algorithmes similaires, comme le sorting par insertion et le sorting par bulles.

L’algorithme est assez simple. La collection est itérée pour trouver le plus petit élément, qui est ensuite déplacé au début de la collection. Le processus est répété en commençant au deuxième élément et ainsi de suite jusqu’à ce que tous les éléments aient été déplacés. L’algorithme n’est pas très efficace, mais pour tout ce que vous allez afficher dans une interface utilisateur, peu importe. Cependant, en termes de nombre d’opérations de déplacement, il est assez efficace.

Voici une méthode d’extension qui, pour simplifier, exige que les éléments implémentent IComparable . D’autres options utilisent un IComparer ou un Func .

 public static class ObservableCollectionExtensions { public static void Sort(this ObservableCollection collection) where T : IComparable { 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); } } } 

Trier une collection consiste simplement à appeler la méthode d'extension:

 var collection = new ObservableCollection(...); collection.Sort(); 

Pour améliorer un peu la méthode d’extension sur xr280xr answer, j’ai ajouté un paramètre bool facultatif pour déterminer si le sorting est décroissant ou non. J’ai également inclus la suggestion de Carlos P dans le commentaire de cette réponse. S’il vous plaît voir ci-dessous.

 public static void Sort(this ObservableCollection source, Func keySelector, bool desc = false) { if (source == null) return; Comparer comparer = Comparer.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); } } } 

Avez-vous besoin de garder votre collection sortingée à tout moment? Lorsque vous récupérez les paires, avez-vous besoin qu’elles soient toujours sortingées ou seulement pour quelques instants (peut-être juste pour la présentation)? Quelle est la taille de votre collection? De nombreux facteurs peuvent vous aider à décider de la méthode à utiliser.

Si vous souhaitez que la collection soit sortingée à tout moment, même lorsque vous insérez ou supprimez des éléments et que la vitesse d’insertion ne pose pas de problème, vous devez implémenter SortedObservableCollection comme @Gerrie Schenck ou vérifier cette implémentation .

Si vous avez besoin de votre collection sortingée juste pour quelques temps, utilisez:

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

Cela prendra un certain temps pour sortinger la collection, mais même si cela peut être la meilleure solution en fonction de ce que vous en faites.

Ma réponse actuelle a déjà le plus de votes, mais j’ai trouvé un moyen plus efficace de le faire.

 class MyObject { public int id { get; set; } public ssortingng title { get; set; } } ObservableCollection myCollection = new ObservableCollection(); //add stuff to collection // . // . // . myCollection = new ObservableCollection( myCollection.OrderBy(n => n.title, Comparer.Create( (x, y) => (Utils.Utils.LogicalSsortingngCompare(x, y))))); 

Créez une nouvelle classe SortedObservableCollection , SortedObservableCollection -la de ObservableCollection et implémentez IComparable> .

Un moyen serait de le convertir en une liste, puis d’appeler Sort (), en fournissant un délégué de comparaison. Quelque chose comme:-

(non testé)

 my_collection.ToList().Sort((left, right) => left == right ? 0 : (left > right ? -1 : 1)); 

Que diable, je vais vous donner une réponse rapide et bien faite … ça ressemble un peu à d’autres implémentations ici, mais je vais append n’importe quoi:

(à peine testé, j’espère que je ne me gêne pas)

Précisons d’abord certains objectives (mes hypothèses):

1) Doit sortinger ObservableCollection en place, pour conserver les notifications, etc.

2) Ne doit pas être horriblement inefficace (c.-à-d. Quelque chose proche de la “bonne” efficacité du sorting)

 public static class Ext { public static void Sort(this ObservableCollection src) where T : IComparable { // 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 } } 

Aucune de ces réponses n’a fonctionné dans mon cas. Soit parce que la liaison est vissée, soit parce que le codage est tellement compliqué que c’est un cauchemar, ou que la réponse est simplement cassée. Alors, voici encore une autre réponse plus simple que je pensais. C’est beaucoup moins de code et il rest la même collection observable avec une méthode supplémentaire de type this.sort. Faites-moi savoir s’il y a une raison pour laquelle je ne devrais pas le faire de cette façon (efficacité, etc.)?

 public class ScoutItems : ObservableCollection { public void Sort(SortDirection _sDir, ssortingng _sItem) { //TODO: Add logic to look at _sItem and decide what property to sort on IEnumerable 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); } } } 

… Où ScoutItem est ma classe publique. Juste semblait beaucoup plus simple. Avantage ajouté: cela fonctionne réellement et ne joue pas avec les liaisons ni ne renvoie une nouvelle collection etc.

Bon, comme j’avais des problèmes pour que ObservableSortedList fonctionne avec XAML, je suis allé de l’avant et j’ai créé SortingObservableCollection . Il hérite de ObservableCollection, donc il fonctionne avec XAML et je l’ai testé à 98% de couverture de code. Je l’ai utilisé dans mes propres applications, mais je ne vous promets pas qu’il est exempt de bogues. N’hésitez pas à consortingbuer. Voici l’exemple d’utilisation du code:

 var collection = new SortingObservableCollection(Comparer.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 

C’est un PCL, il devrait donc fonctionner avec Windows Store, Windows Phone et .NET 4.5.1.

C’est ce que je fais avec les extensions OC:

  ///  /// Synches the collection items to the target collection items. /// This does not observe sort order. ///  ///  /// The items. /// The updated collection. public static void SynchCollection(this IList source, IEnumerable 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)); } ///  /// Synches the collection items to the target collection items. ///  ///  /// The source. /// The updated collection. /// if set to true [can sort]. public static void SynchCollection(this ObservableCollection source, IList 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); } } 

Cela a fonctionné pour moi, l’a trouvé il y a longtemps quelque part.

 // SortableObservableCollection public class SortableObservableCollection : ObservableCollection { public SortableObservableCollection(List list) : base(list) { } public SortableObservableCollection() { } public void Sort(Func 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(Func keySelector, IComparer comparer) { ApplySort(Items.OrderBy(keySelector, comparer)); } private void ApplySort(IEnumerable sortedItems) { var sortedItemsList = sortedItems.ToList(); foreach (var item in sortedItemsList) { Move(IndexOf(item), sortedItemsList.IndexOf(item)); } } } 

Usage:

 MySortableCollection.Sort(x => x, System.ComponentModel.ListSortDirection.Ascending); 

J’avais besoin de pouvoir sortinger par plusieurs choses et non pas une seule. Cette réponse est basée sur certaines des autres réponses, mais elle permet un sorting plus complexe.

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

Lorsque vous l'utilisez, passez une série d'appels OrderBy / ThenBy. Comme ça:

 Children.Sort(col => col.OrderByDescending(xx => xx.ItemType == "drive") .ThenByDescending(xx => xx.ItemType == "folder") .ThenBy(xx => xx.Path)); 
 var collection = new ObservableCollection(); collection.Add(7); collection.Add(4); collection.Add(12); collection.Add(1); collection.Add(20); // ascending collection = new ObservableCollection(collection.OrderBy(a => a)); // descending collection = new ObservableCollection(collection.OrderByDescending(a => a));