我如何sorting可观察的集合?

我有一个下面的课:

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

我把它放在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")); 

问:我如何按键sorting?

操作编辑:正如许多人已经正确指出原来的答案不会返回相同的集合,(原来更侧重于sorting字典部分的Q)。 请参阅底部的编辑处理可观察集合的sorting。 原来在这里仍然收到票

您可以使用linq作为下面的doSort方法说明。 一个快速的代码片段:产生

3:xey 6:fty 7:aaa

或者,您可以在集合本身上使用扩展方法

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

编辑

要返回一个ObservableCollection, 可以使用这个实现在sortedOC上调用.ToObservableCollection。

操作编辑sorting一个observable并返回相同的对象sorting可以使用扩展方法完成。 对于更大的集合,请注意收集更改通知的数量,例如

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

用法:用一个观察者(用一个Person类来保持简单)

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

从上面输出:
删除指数为0的凯蒂51岁
Katy在索引8添加了51岁
在索引3删除了41岁的玛丽
在索引7添加了41岁的玛丽
在指数3删除简20岁
补充指数5岁的简20岁
指数3删除吉姆39岁
吉姆在第6位添加了39岁
在指数4删除简20岁
补充指数5岁的简20岁

Person类实现了IComparable和IEquatable,后者用于最小化对集合的更改,从而减less所提出的更改通知的数量

这个简单的扩展工作对我来说很好。 我只是要确保MyObjectIComparable 。 在MyObjects的可观察集合上调用sorting方法时,将调用MyObject上的CompareTo方法,该方法调用Logical Sort方法。 虽然它没有在这里发布的其他答案的所有钟声,这正是我所需要的。

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

我知道这个问题是旧的,但只是发生在Googlesearch,发现了一个相关的博客条目,提供了比这里更好的答案:

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

UPDATE

@romkyns在注释中指出的ObservableSortedList自动维护sorting顺序。

实现一个可观察的集合,按照sorting顺序维护其项目。 特别是,导致订单更改的项目属性的更改正确处理。

不过也要注意这句话

由于涉及接口比较复杂,文档相对较差,可能会出现错误(请参阅https://stackoverflow.com/a/5883947/33080 )。

你可以使用这个简单的方法:

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

你可以这样sorting:

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

更多详情: http : //jaider.net/2011-05-04/sort-a-observablecollection/

我喜欢上面的“Richie”博客上的气泡sorting扩展方法,但是我不一定要对整个对象进行sorting。 我更经常想要sorting对象的特定属性。 所以我修改它以接受OrderBy的方式键select器,以便您可以select要sorting的属性:

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

你可以调用与OrderBy相同的方式,除非它将对ObservableCollection的现有实例进行sorting,而不是返回一个新的集合:

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

WPF使用ListCollectionView提供实时的现成的sorting

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

一旦这个初始化完成,没有什么可做的了。 与被动types相比,优势在于ListCollectionView以对开发人员透明的方式完成所有繁重的工作。 新项目会自动按正确的sorting顺序排列。 任何派生自T的IComparer的类都适用于自定义sorting属性。

有关文档和其他function,请参阅ListCollectionView 。

一种变化就是使用selectsortingalgorithm对集合进行sorting 。 元素使用Move方法移动到位。 每一步移动都会触发带有NotifyCollectionChangedAction.MoveCollectionChanged事件(也包含属性名为Item[] PropertyChanged )。

这个algorithm有一些很好的属性:

  • 该algorithm可以实现为稳定的sorting。
  • 在集合中移动的项目(例如,被触发的CollectionChanged事件)的数量几乎总是less于其他类似的algorithm,如插入sorting和冒泡sorting。

这个algorithm很简单。 迭代该集合以查找最小的元素,然后将其移至集合的起始位置。 该过程从第二个元素开始重复,直到所有元素都移动到位。 该algorithm不是非常有效,但对于任何你要在用户界面中显示的任何东西都没有关系。 但是,从移动操作的数量来看,这是非常有效的。

这里是一个扩展方法,为了简单起见,要求元素实现IComparable<T> 。 其他选项使用IComparer<T>Func<T, T, Boolean>

 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集合只是调用扩展方法的一个问题:

 var collection = new ObservableCollection<String>(...); collection.Sort(); 

我想添加到NeilW的答案 。 合并一个类似于order by的方法。 添加这个方法作为扩展:

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

并使用像:

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

@ NielW的答案是要走的路,真正的就地sorting。 我想添加一个稍微改变的解决scheme,让您绕过不得不使用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); } } 

现在你可以像任何LINQ方法那样调用它:

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

为了改善xr280xr答案的扩展方法,我添加了一个可选的bool参数来确定sorting是否降序。 我还包括了卡洛斯·P在这个答复的评论中提出的build议。 请参阅下文。

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

你需要保持你的collections在任何时候分类吗? 当检索对,你需要他们总是sorting,或者只是几次(也许只是为了呈现)? 你认为你的collections有多大? 有很多因素可以帮助你决定使用巫术的方法。

如果你需要集合在任何时候被sorting,即使你插入或删除元素和插入速度不是一个问题,也许你应该实现某种SortedObservableCollection像@Gerrie Schenck提到或检查出这个实现 。

如果您需要将您的collections分类几次,请使用:

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

这将需要一些时间来sorting收集,但即使如此,这可能是最好的解决scheme,取决于你在做什么。

我现在的答案已经有了最多的选票,但是我发现了一个更好,更现代化的方式。

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

创build一个新类SortedObservableCollection ,从ObservableCollection派生它并实现IComparable<Pair<ushort, string>>

一种方法是将其转换为List,然后调用Sort(),提供一个比较委托。 就像是:-

(另)

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

到底什么事情,我会扔在一个快速拼凑在一起的答案…它看起来有点像其他一些实现在这里,但我会添加它anywho:

(勉强testing,希望我不尴尬自己)

让我们先说一些目标(我的假设):

1)必须对ObservableCollection<T>进行sorting,以保持通知等。

2)不可能是非常低效的(即, 接近标准的“良好”分选效率的东西)

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

这些答案都没有在我的情况下工作。 要么是因为它搞乱了绑定,要么需要这么多的额外的编码,这是一种噩梦,或者答案只是被打破了。 所以,我想这是另一个更简单的答案。 它的代码less了很多,而且它仍然是一个可观察的集合,还有一个额外的这种types的方法。 让我知道如果有什么理由我不应该这样做(效率等)?

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

ScoutItem是我的公开课。 只是似乎更简单。 增加的好处:它实际上工作,并没有搞乱绑定或返回一个新的集合等

好的,因为我有问题让ObservableSortedList与XAML一起工作,我继续前进并创build了SortingObservableCollection 。 它inheritance自ObservableCollection,所以它可以和XAML一起工作,我已经用unit testing了它的代码覆盖率为98%。 我在我自己的应用程序中使用它,但我不会保证它是无错的。 随意贡献。 以下是示例代码的用法:

 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 

这是一个PCL,所以它应该与Windowsapp store,Windows Phone和.NET 4.5.1一起工作。

这就是我使用OC扩展所做的事情:

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

这工作对我来说,发现很久以前的某个地方。

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

用法:

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

我需要能够通过多种事物来sorting,而不仅仅是一个。 这个答案是基于一些其他的答案,但它允许更复杂的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); } } 

当你使用它时,传入一系列的OrderBy / ThenBy调用。 喜欢这个:

 Children.Sort(col => col.OrderByDescending(xx => xx.ItemType == "drive") .ThenByDescending(xx => xx.ItemType == "folder") .ThenBy(xx => xx.Path)); 
 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));