Come posso ordinare una collezione osservabile?

Ho una class seguente:

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

Che ho inserito in 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")); 

D: Come posso ordinare per chiave?

OP Modifica: come molti hanno correttamente sottolineato la risposta originale non restituisce la stessa collezione, (originariamente focalizzata più sull’ordinamento della parte del dizionario della Q). Si prega di vedere la modifica in fondo dove indirizzo l’ordinamento di una collezione osservabile. L’originale è rimasto qui mentre continua a ricevere voti

Puoi usare linq come illustrato nel seguente metodo doSort. Un rapido snippet di codice: produce

3: xey 6: fty 7: aaa

In alternativa è ansible utilizzare un metodo di estensione sulla raccolta stessa

 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 string ToString() { return this.Key + ":" + this.Value; } } 

MODIFICARE

Per restituire ObservableCollection, chiama. ToObservableCollection su ordinatiOC utilizzando, ad esempio, questa implementazione .

OP EDIT Ordinare un osservabile e restituire lo stesso object ordinato può essere fatto usando un metodo di estensione. Per le collezioni più grandi, fai attenzione al numero di notifiche cambiate, ad es

 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++; } } } 

utilizzo: campione con un osservatore (usato una class Person per mantenerlo semplice)

 public class Person:IComparable,IEquatable { 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() { 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(); }; 

Uscita dall'alto:
rimosso Katy all'età di 51 anni con indice 0
ha aggiunto Katy di 51 anni con indice 8
rimosso Mary di 41 anni con indice 3
ha aggiunto Mary di 41 anni con indice 7
rimosso Jane di 20 anni con indice 3
ha aggiunto Jane di 20 anni con indice 5
rimosso Jim di 39 anni con indice 3
aggiunse Jim all'età di 39 anni all'indice 6
rimosso Jane di 20 anni con indice 4
ha aggiunto Jane di 20 anni con indice 5

La class Person implementa sia IComparable che IEquatable, quest'ultima viene utilizzata per minimizzare le modifiche alla raccolta in modo da ridurre il numero di notifiche di modifiche generate

Questa semplice estensione ha funzionato magnificamente per me. Dovevo solo assicurarmi che MyObject fosse IComparable . Quando il metodo sort viene chiamato sulla raccolta osservabile di MyObjects , viene chiamato il metodo CompareTo su MyObject , che chiama il metodo Logical Sort. Mentre non ha tutte le campane e i fischi delle altre risposte pubblicate qui, è esattamente quello di cui avevo bisogno.

 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(); 

So che questa domanda è vecchia, ma mi sono imbattuta in esso mentre facevo ricerche su Google e ho trovato un post di blog pertinente che fornisce una risposta migliore rispetto a quelli qui riportati:

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

AGGIORNARE

L’ ObservableSortedList che @romkyns indica nei commenti mantiene automaticamente l’ordinamento.

Implementa una collezione osservabile che mantiene i suoi articoli in ordine. In particolare, le modifiche alle proprietà degli articoli che comportano modifiche degli ordini sono gestite correttamente.

Comunque nota anche l’osservazione

Può essere bacato a causa della complessità comparativa dell’interfaccia in questione e della relativa documentazione relativamente scarsa (vedere https://stackoverflow.com/a/5883947/33080 ).

Puoi usare questo semplice metodo:

 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); } 

Puoi ordinare in questo modo:

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

Ulteriori dettagli: http://jaider.net/2011-05-04/sort-a-observablecollection/

Mi è piaciuto l’approccio del metodo di estensione di tipo bubble sul blog di “Richie” qui sopra, ma non necessariamente voglio solo ordinare il confronto dell’intero object. Preferisco più spesso ordinare su una proprietà specifica dell’object. Così l’ho modificato per accettare un selettore di chiave come OrderBy, così puoi scegliere quale proprietà ordinare:

  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); } } } } 

Che chiamereste nello stesso modo in cui chiamereste OrderBy, tranne che ordinerà l’istanza esistente di ObservableCollection invece di restituire una nuova raccolta:

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

WPF fornisce l’ordinamento in tempo reale utilizzando la class ListCollectionView

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

Una volta completata questa inizializzazione, non c’è più nulla da fare. Il vantaggio rispetto a un ordinamento passivo è che ListCollectionView esegue tutti i lavori pesanti in modo trasparente per lo sviluppatore. I nuovi elementi vengono automaticamente inseriti nel loro corretto ordinamento. Qualsiasi class derivata da IComparer di T è adatta per la proprietà di ordinamento personalizzata.

Vedi ListCollectionView per la documentazione e altre funzionalità.

Vorrei aggiungere alla risposta di NeilW . Per incorporare un metodo che assomiglia al orderby. Aggiungi questo metodo come un’estensione:

 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); } 

E usare come:

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

@ La risposta di NielW è la strada da percorrere, per un vero ordinamento sul posto. Volevo aggiungere una soluzione leggermente modificata che ti consentisse di bypassare l’uso di 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); } } 

ora puoi chiamarlo come la maggior parte dei metodi LINQ:

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

Una variante è dove si ordina la raccolta in atto usando un algoritmo di selezione . Gli elementi vengono spostati in posizione usando il metodo Move . Ogni mossa genererà l’evento CollectionChanged con NotifyCollectionChangedAction.Move (e anche PropertyChanged con il nome della proprietà Item[] ).

Questo algoritmo ha alcune proprietà interessanti:

  • L’algoritmo può essere implementato come un ordinamento stabile.
  • Il numero di elementi spostati nella raccolta (ad es CollectionChanged eventi CollectionChanged licenziati) è quasi sempre inferiore ad altri algoritmi simili come l’ordinamento per inserimento e l’ordinamento a bolle.

L’algoritmo è abbastanza semplice. La raccolta viene iterata per trovare l’elemento più piccolo che viene poi spostato all’inizio della raccolta. Il processo viene ripetuto a partire dal secondo elemento e così via fino a quando tutti gli elementi non sono stati spostati in posizione. L’algoritmo non è terribilmente efficiente, ma per qualsiasi cosa tu debba mostrare in un’interfaccia utente non dovrebbe avere importanza. Tuttavia, in termini di numero di operazioni di spostamento è abbastanza efficiente.

Ecco un metodo di estensione che per semplicità richiede che gli elementi implementino IComparable . Altre opzioni utilizzano IComparer o 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); } } } 

L'ordinamento di una raccolta è semplicemente una questione di invocare il metodo di estensione:

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

Per migliorare un po ‘il metodo di estensione sulla risposta xr280xr ho aggiunto un parametro bool opzionale per determinare se l’ordinamento è discendente o meno. Ho incluso anche il suggerimento fatto da Carlos P nel commento a quella risposta. Vedi sotto.

 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); } } } 

Hai bisogno di mantenere la tua collezione ordinata in ogni momento? Quando recuperi le coppie, hai bisogno che siano sempre ordinate, o è solo per poche volte (forse solo per la presentazione)? Quanto ti aspetti che la tua collezione sia grande? Ci sono molti fattori che possono aiutarti a decidere come usare il metodo strega.

Se è necessario ordinare la raccolta in qualsiasi momento, anche quando si inseriscono o eliminano elementi e la velocità di inserimento non è un problema, forse è necessario implementare una sorta di SortedObservableCollection come @Gerrie Schenck menzionata o verificare questa implementazione .

Se hai bisogno della tua raccolta ordinata solo per poche volte, usa:

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

Ci vorrà del tempo per ordinare la collezione, ma anche così, potrebbe essere la soluzione migliore a seconda di cosa la stai facendo.

La mia risposta attuale ha già il maggior numero di voti, ma ho trovato un modo migliore e più moderno per farlo.

 class MyObject { public int id { get; set; } public string 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.LogicalStringCompare(x, y))))); 

Crea una nuova class SortedObservableCollection , SortedObservableCollection da ObservableCollection e implementa IComparable> .

Un modo sarebbe convertirlo in un elenco e quindi chiamare Sort (), fornendo un delegato di confronto. Qualcosa di simile a:-

(non testato)

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

Che diamine, ti darò una risposta rapida e insieme acctriggersnte … sembra un po ‘come alcune altre implementazioni qui, ma aggiungerò qualcos’altro:

(a malapena testato, spero di non essere imbarazzante)

Per prima cosa stabiliamo alcuni obiettivi (le mie ipotesi):

1) Devi ordinare ObservableCollection in luogo, per mantenere le notifiche, ecc.

2) Non deve essere orribilmente inefficiente (ad esempio, qualcosa che si avvicina all’efficienza di classificazione standard “buona”)

 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 } } 

Nessuna di queste risposte ha funzionato nel mio caso. O perché fa a pezzi il binding, o richiede così tanto codice aggiuntivo che è una specie di incubo, o la risposta è solo spezzata. Quindi, ecco un’altra risposta più semplice che ho pensato. È molto meno codice e rimane la stessa collezione osservabile con un ulteriore tipo di metodo this.sort. Fammi sapere se c’è qualche ragione per cui non dovrei farlo in questo modo (efficienza, ecc.)?

 public class ScoutItems : ObservableCollection { public void Sort(SortDirection _sDir, string _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); } } } 

… Dove ScoutItem è la mia class pubblica. Sembrava molto più semplice. Vantaggio aggiunto: funziona davvero e non rovina i binding o restituisce una nuova collezione, ecc.

Bene, dato che avevo problemi a far funzionare ObservableSortedList con XAML, sono andato avanti e ho creato SortingObservableCollection . È ereditato da ObservableCollection, quindi funziona con XAML e l’ho testato con il 98% di copertura del codice. L’ho usato nelle mie app, ma non prometterò che sia privo di errori. Sentiti libero di contribuire. Ecco un esempio di utilizzo del codice:

 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 

È un PCL, quindi dovrebbe funzionare con Windows Store, Windows Phone e .NET 4.5.1.

Questo è quello che faccio con le estensioni 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); } } 

Questo ha funzionato per me, l’ho trovato molto tempo fa da qualche parte.

 // 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)); } } } 

Uso:

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

Avevo bisogno di essere in grado di ordinare più cose, non solo una. Questa risposta è basata su alcune delle altre risposte ma consente un ordinamento più complesso.

 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); } } 

Quando lo usi, passa una serie di chiamate OrderBy / ThenBy. Come questo:

 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));