我低于ObservableCollection<string>
。我需要按字母顺序排序。
private ObservableCollection<string> _animals = new ObservableCollection<string>
{
"Cat", "Dog", "Bear", "Lion", "Mouse",
"Horse", "Rat", "Elephant", "Kangaroo", "Lizard",
"Snake", "Frog", "Fish", "Butterfly", "Human",
"Cow", "Bumble Bee"
};
我试过了_animals.OrderByDescending
。但我不知道如何正确使用它。
_animals.OrderByDescending(a => a.<what_is_here_?>);
我该怎么做?
答案 0 :(得分:95)
基本上,如果需要显示已排序的集合,请考虑使用CollectionViewSource
类:将其Source
属性赋值(“bind”)到源集合 - {的一个实例{1}}上课。
这个想法是CollectionViewSource
class provides an instance of the CollectionView
class。这是原始(源)集合的“投影”,但应用了排序,过滤等。
参考文献:
WPF 4.5为ObservableCollection<T>
引入了“实时整形”功能。
参考文献:
如果仍然需要对CollectionViewSource
类的实例进行排序,可以采用以下方式。
ObservableCollection<T>
类本身没有sort方法。但是,可以重新创建集合以对项目进行排序:
ObservableCollection<T>
请注意// Animals property setter must raise "property changed" event to notify binding clients.
// See INotifyPropertyChanged interface for details.
Animals = new ObservableCollection<string>
{
"Cat", "Dog", "Bear", "Lion", "Mouse",
"Horse", "Rat", "Elephant", "Kangaroo",
"Lizard", "Snake", "Frog", "Fish",
"Butterfly", "Human", "Cow", "Bumble Bee"
};
...
Animals = new ObservableCollection<string>(Animals.OrderBy(i => i));
和OrderBy()
方法(与其他LINQ扩展方法一样)不要修改源集合!而是创建一个新序列(即实现OrderByDescending()
接口的类的新实例)。因此,有必要重新创建集合。
答案 1 :(得分:34)
我知道这是一个老问题,但是第一个google结果为“sort observablecollection”,所以认为值得留下我的两分钱。
方式
我的方法是从List<>
开始构建ObservableCollection<>
,对其进行排序(通过Sort()
方法,more on msdn)以及{{1}已排序,使用List<>
方法重新排序ObservableCollection<>
。
Move()
public static void Sort<T>(this ObservableCollection<T> collection, Comparison<T> comparison)
{
var sortableList = new List<T>(collection);
sortableList.Sort(comparison);
for (int i = 0; i < sortableList.Count; i++)
{
collection.Move(collection.IndexOf(sortableList[i]), i);
}
}
答案 2 :(得分:7)
我为ObservableCollection
创建了一个扩展方法public static void MySort<TSource,TKey>(this ObservableCollection<TSource> observableCollection, Func<TSource, TKey> keySelector)
{
var a = observableCollection.OrderBy(keySelector).ToList();
observableCollection.Clear();
foreach(var b in a)
{
observableCollection.Add(b);
}
}
它似乎有用,您不需要实现IComparable
答案 3 :(得分:5)
我看着这些,我把它分类,然后它打破了绑定,如上所述。提出这个解决方案,虽然比你的大多数人简单,但似乎做了我想做的事情,
public static ObservableCollection<string> OrderThoseGroups( ObservableCollection<string> orderThoseGroups)
{
ObservableCollection<string> temp;
temp = new ObservableCollection<string>(orderThoseGroups.OrderBy(p => p));
orderThoseGroups.Clear();
foreach (string j in temp) orderThoseGroups.Add(j);
return orderThoseGroups;
}
答案 4 :(得分:5)
这是ObservableCollection<T>
,它会在更改时自动排序,仅在必要时触发排序,并且仅触发单个移动集合更改操作。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace ConsoleApp4
{
using static Console;
public class SortableObservableCollection<T> : ObservableCollection<T>
{
public Func<T, object> SortingSelector { get; set; }
public bool Descending { get; set; }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action != NotifyCollectionChangedAction.Reset
&& e.Action != NotifyCollectionChangedAction.Move
&& SortingSelector != null)
{
var query = this
.Select((item, index) => (Item: item, Index: index));
query = Descending
? query.OrderBy(tuple => SortingSelector(tuple.Item))
: query.OrderByDescending(tuple => SortingSelector(tuple.Item));
var map = query.Select((tuple, index) => (OldIndex:tuple.Index, NewIndex:index))
.Where(o => o.OldIndex != o.NewIndex);
using (var enumerator = map.GetEnumerator())
if (enumerator.MoveNext())
Move(enumerator.Current.OldIndex, enumerator.Current.NewIndex);
}
}
}
//USAGE
class Program
{
static void Main(string[] args)
{
var xx = new SortableObservableCollection<int>() { SortingSelector = i => i };
xx.CollectionChanged += (sender, e) =>
WriteLine($"action: {e.Action}, oldIndex:{e.OldStartingIndex},"
+ " newIndex:{e.NewStartingIndex}, newValue: {xx[e.NewStartingIndex]}");
xx.Add(10);
xx.Add(8);
xx.Add(45);
xx.Add(0);
xx.Add(100);
xx.Add(-800);
xx.Add(4857);
xx.Add(-1);
foreach (var item in xx)
Write($"{item}, ");
}
}
}
输出:
action: Add, oldIndex:-1, newIndex:0, newValue: 10
action: Add, oldIndex:-1, newIndex:1, newValue: 8
action: Move, oldIndex:1, newIndex:0, newValue: 8
action: Add, oldIndex:-1, newIndex:2, newValue: 45
action: Add, oldIndex:-1, newIndex:3, newValue: 0
action: Move, oldIndex:3, newIndex:0, newValue: 0
action: Add, oldIndex:-1, newIndex:4, newValue: 100
action: Add, oldIndex:-1, newIndex:5, newValue: -800
action: Move, oldIndex:5, newIndex:0, newValue: -800
action: Add, oldIndex:-1, newIndex:6, newValue: 4857
action: Add, oldIndex:-1, newIndex:7, newValue: -1
action: Move, oldIndex:7, newIndex:1, newValue: -1
-800, -1, 0, 8, 10, 45, 100, 4857,
答案 5 :(得分:3)
OrderByDescending
的参数是一个返回键的函数。在您的情况下,键是字符串本身:
var result = _animals.OrderByDescending(a => a);
例如,如果你想按长度排序,你会写:
var result = _animals.OrderByDescending(a => a.Length);
答案 6 :(得分:3)
_animals.OrderByDescending(a => a.<what_is_here_?>);
如果动物是动物对象的列表,您可以使用属性来对列表进行排序。
public class Animal
{
public int ID {get; set;}
public string Name {get; set;}
...
}
ObservableCollection<Animal> animals = ...
animals = animals.OrderByDescending(a => a.Name);
答案 7 :(得分:3)
myObservableCollection.ToList().Sort((x, y) => x.Property.CompareTo(y.Property));
答案 8 :(得分:2)
/// <summary>
/// Sorts the collection.
/// </summary>
/// <typeparam name="T">The type of the elements of the collection.</typeparam>
/// <param name="collection">The collection to sort.</param>
/// <param name="comparison">The comparison used for sorting.</param>
public static void Sort<T>(this ObservableCollection<T> collection, Comparison<T> comparison = null)
{
var sortableList = new List<T>(collection);
if (comparison == null)
sortableList.Sort();
else
sortableList.Sort(comparison);
for (var i = 0; i < sortableList.Count; i++)
{
var oldIndex = collection.IndexOf(sortableList[i]);
var newIndex = i;
if (oldIndex != newIndex)
collection.Move(oldIndex, newIndex);
}
}
此解决方案基于Marco's answer。我有some problems他的解决方案,因此只有在索引实际发生变化时才调用Move
来改进它。这应该可以提高性能并解决链接问题。
答案 9 :(得分:2)
此扩展方法无需对整个列表进行排序。
相反,它会将每个新项目插入到位。
因此列表始终保持排序状态。
事实证明,此方法仅在集合更改时由于缺少通知而导致许多其他方法失败时起作用。而且速度很快。
要使用:
// Call on dispatcher.
ObservableCollection<MyClass> collectionView = new ObservableCollection<MyClass>();
var p1 = new MyClass() { Key = "A" }
var p2 = new MyClass() { Key = "Z" }
var p3 = new MyClass() { Key = "D" }
collectionView.InsertInPlace(p1, o => o.Key);
collectionView.InsertInPlace(p2, o => o.Key);
collectionView.InsertInPlace(p3, o => o.Key);
// The list will always remain ordered on the screen, e.g. "A, D, Z" .
// Insertion speed is Log(N) as it uses a binary search.
扩展方法:
/// <summary>
/// Inserts an item into a list in the correct place, based on the provided key and key comparer. Use like OrderBy(o => o.PropertyWithKey).
/// </summary>
public static void InsertInPlace<TItem, TKey>(this ObservableCollection<TItem> collection, TItem itemToAdd, Func<TItem, TKey> keyGetter)
{
int index = collection.ToList().BinarySearch(keyGetter(itemToAdd), Comparer<TKey>.Default, keyGetter);
collection.Insert(index, itemToAdd);
}
以及二进制搜索扩展方法:
/// <summary>
/// Binary search.
/// </summary>
/// <returns>Index of item in collection.</returns>
/// <notes>This version tops out at approximately 25% faster than the equivalent recursive version. This 25% speedup is for list
/// lengths more of than 1000 items, with less performance advantage for smaller lists.</notes>
public static int BinarySearch<TItem, TKey>(this IList<TItem> collection, TKey keyToFind, IComparer<TKey> comparer, Func<TItem, TKey> keyGetter)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
int lower = 0;
int upper = collection.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer.Compare(keyToFind, keyGetter.Invoke(collection[middle]));
if (comparisonResult == 0)
{
return middle;
}
else if (comparisonResult < 0)
{
upper = middle - 1;
}
else
{
lower = middle + 1;
}
}
// If we cannot find the item, return the item below it, so the new item will be inserted next.
return lower;
}
答案 10 :(得分:1)
我也想分享自己的想法,因为我遇到了同样的问题。
嗯,只需回答这个问题就可以了:
1-像这样将可扩展性添加到可观察的集合类中:
namespace YourNameSpace
{
public static class ObservableCollectionExtension
{
public static void OrderByReference<T>(this ObservableCollection<T> collection, List<T> comparison)
{
for (int i = 0; i < comparison.Count; i++)
{
if (!comparison.ElementAt(i).Equals(collection.ElementAt(i)))
collection.Move(collection.IndexOf(comparison[i]), i);
}
}
public static void InsertInPlace<T>(this ObservableCollection<T> collection, List<T> comparison, T item)
{
int index = comparison.IndexOf(item);
comparison.RemoveAt(index);
collection.OrderByReference(comparison);
collection.Insert(index, item);
}
}
}
2-然后像这样使用它:
_animals.OrderByReference(_animals.OrderBy(x => x).ToList());
这将更改您的ObservableCollection,可以使用linq,并且不会更改绑定!
额外:
我根据自己的喜好扩展了@Marco和@Contango的答案。首先,我想到直接使用列表作为比较,因此您将获得以下信息:
public static void OrderByReference<T>(this ObservableCollection<T> collection, List<T> comparison)
{
for (int i = 0; i < comparison.Count; i++)
{
collection.Move(collection.IndexOf(comparison[i]), i);
}
}
并像这样使用:
YourObservableCollection.OrderByReference(YourObservableCollection.DoYourLinqOrdering().ToList());
然后我想到了,因为这总是移动所有东西并触发ObservableCollection中的移动,为什么不比较对象是否已经存在,这就是我从Equals比较器开始就已经提出的内容。
将对象添加到正确的位置听起来也不错,但是我警告了一种简单的方法。所以我想到了:
public static void InsertInPlace<T>(this ObservableCollection<T> collection, List<T> comparison, T item)
{
collection.Insert(comparison.IndexOf(item), item);
}
您发送了一个列表,其中包含所需的新对象以及该新对象,因此您需要创建一个列表,然后添加该新对象,如下所示:
var YourList = YourObservableCollection.ToList();
var YourObject = new YourClass { ..... };
YourList.Add(YourObject);
YourObservableCollection.InsertInPlace(YourList.DoYourLinqOrdering().ToList(), YourObject);
但是由于ObservableCollection的顺序可能与列表不同,因为在“ DoYourLinqOrdering()”中进行了选择(如果以前未对集合进行排序,则会发生这种情况),所以我添加了第一个扩展(OrderByReference)您可以在答案开头看到插入内容中的内容。如果不需要移动itens arround,不会花很长时间,因此我在使用它时没有遇到问题。
随着性能的提高,我已经通过检查每个方法完成所需的时间来比较了这些方法,因此并不理想,但是无论如何,我已经用20000 itens测试了一个可观察的集合。对于OrderByReference,我并没有看到通过添加Equal对象检查器而在性能上有很大的不同,但是如果不是所有的iten都需要移动,它会更快,并且不会在collecitonChanged上引发不必要的Move事件,因此就可以了。因为InsertInPlace是同一件事,如果ObservableCollection已经排序,则仅检查对象是否在正确的位置比移动所有ites快,因此,如果只是通过对象,则时间差异不大。等于语句,您将受益于确保一切都在应有的位置。
请注意,如果将这种扩展方式与不适用的对象或具有更多或更少对象的列表一起使用,则会得到ArgumentOutOfRangeException或其他一些异常行为。
希望这对某人有帮助!
答案 11 :(得分:0)
我对某个类字段(距离)进行了排序。
public class RateInfo
{
public string begin { get; set; }
public string end { get; set; }
public string price { get; set; }
public string comment { get; set; }
public string phone { get; set; }
public string ImagePath { get; set; }
public string what { get; set; }
public string distance { get; set; }
}
public ObservableCollection<RateInfo> Phones { get; set; }
public List<RateInfo> LRate { get; set; }
public ObservableCollection<RateInfo> Phones { get; set; }
public List<RateInfo> LRate { get; set; }
......
foreach (var item in ph)
{
LRate.Add(new RateInfo { begin = item["begin"].ToString(), end = item["end"].ToString(), price = item["price"].ToString(), distance=kilom, ImagePath = "chel.png" });
}
LRate.Sort((x, y) => x.distance.CompareTo(y.distance));
foreach (var item in LRate)
{
Phones.Add(item);
}
答案 12 :(得分:0)
这里与Shimmy的类略有不同,该类用于收集已经实现了著名的IComparable<T>
interface的类。在这种情况下,“ order by”选择器是隐式的。
public class SortedObservableCollection<T> : ObservableCollection<T> where T : IComparable<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action != NotifyCollectionChangedAction.Reset &&
e.Action != NotifyCollectionChangedAction.Move &&
e.Action != NotifyCollectionChangedAction.Remove)
{
var query = this.Select((item, index) => (Item: item, Index: index)).OrderBy(tuple => tuple.Item, Comparer.Default);
var map = query.Select((tuple, index) => (OldIndex: tuple.Index, NewIndex: index)).Where(o => o.OldIndex != o.NewIndex);
using (var enumerator = map.GetEnumerator())
{
if (enumerator.MoveNext())
{
base.MoveItem(enumerator.Current.OldIndex, enumerator.Current.NewIndex);
}
}
}
}
// (optional) user is not allowed to move items in a sorted collection
protected override void MoveItem(int oldIndex, int newIndex) => throw new InvalidOperationException();
protected override void SetItem(int index, T item) => throw new InvalidOperationException();
private class Comparer : IComparer<T>
{
public static readonly Comparer Default = new Comparer();
public int Compare(T x, T y) => x.CompareTo(y);
}
// explicit sort; sometimes needed.
public virtual void Sort()
{
if (Items.Count <= 1)
return;
var items = Items.ToList();
Items.Clear();
items.Sort();
foreach (var item in items)
{
Items.Add(item);
}
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
答案 13 :(得分:0)
如果性能是您的主要关注点,并且您不介意听其他事件,那么这是进行稳定排序的方法:
public static void Sort<T>(this ObservableCollection<T> list) where T : IComparable<T>
{
int i = 0;
foreach (var item in list.OrderBy(x => x))
{
if (!item.Equals(list[i]))
{
list[i] = item;
}
i++;
}
}
就稳定排序而言,我不确定是否有任何更简单,更快捷的方法(至少在理论上如此)。在有序列表上执行ToArray可能会使枚举更快,但空间复杂性更差。您也可以取消Equals
检查,以更快地进行检查,但是我想减少更改通知是值得欢迎的事情。
这也不会破坏任何绑定。
请记住,这引发了一堆Replace
事件,而不是Move(排序操作更期望),并且与此事件相比,引发的事件数量也更有可能我认为大多数UI元素必须已实现IList
,并且对ILists
进行替换应该比Moves更快。但是更多更改的事件意味着更多的屏幕刷新。您必须对其进行测试才能看到其中的含义。
要获得Move
的答案,请see this。甚至在集合中有重复项时,都没有看到更正确的实现方法。