通过C#对ObservableCollection <string>进行排序

Bis*_*han 46 c# sorting observablecollection

我在下面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"
};
Run Code Online (Sandbox Code Playgroud)

我试过了_animals.OrderByDescending.但我不知道如何正确使用它.

_animals.OrderByDescending(a => a.<what_is_here_?>);
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Ser*_*nov 105

介绍

基本上,如果需要显示已排序的集合,请考虑使用CollectionViewSource类:assign("bind")其Source属性到源集合 - ObservableCollection<T>类的实例.

这个想法是,CollectionViewSource类提供的实例CollectionView.这是原始(源)集合的"投影",但应用了排序,过滤等.

参考文献:

现场塑造

WPF 4.5引入了"实时整形"功能CollectionViewSource.

参考文献:

如果仍然需要对类的实例进行排序ObservableCollection<T>,那么这是如何完成的.在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));
Run Code Online (Sandbox Code Playgroud)

额外细节

请注意,OrderBy()OrderByDescending()方法(如其他LINQ的扩展方法)不修改的源集合!他们改为创建一个新序列(即实现IEnumerable<T>接口的类的新实例).因此,有必要重新创建集合.

  • 这可能会破坏使用ObservableCollection的任何绑定. (38认同)
  • @ user401246:打破绑定的意思是,如果你使用Observable集合的Selected项绑定了一个属性,那么在重新分配Collection时将重新开始重新分配,谢尔盖在下一次编辑中澄清了这一点. (3认同)

Mar*_*rco 41

我知道这是一个老问题,但是第一个google结果"sort observablecollection"所以认为值得离开我的两分钱.

方式

我将采用的方式是List<>ObservableCollection<>它开始,对它进行排序(通过它的Sort()方法,更多地在msdn上),并在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

考试

public void TestObservableCollectionSortExtension()
{
    var observableCollection = new ObservableCollection<int>();
    var maxValue = 10;

    // Populate the list in reverse mode [maxValue, maxValue-1, ..., 1, 0]
    for (int i = maxValue; i >= 0; i--)
    {
        observableCollection.Add(i);
    }

    // Assert the collection is in reverse mode
    for (int i = maxValue; i >= 0; i--)
    {
        Assert.AreEqual(i, observableCollection[maxValue - i]);
    }

    // Sort the observable collection
    observableCollection.Sort((a, b) => { return a.CompareTo(b); });

    // Assert elements have been sorted
    for (int i = 0; i < maxValue; i++)
    {
        Assert.AreEqual(i, observableCollection[i]);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 绑定不会被破坏,但是对任何项目的事件的所有订阅(在代码中)都会.最重要的是,从头开始重新创建集合,绑定的UI元素(它可能是列表视图或任何你想要的)可能必须重新创建所有可能需要更多的视觉项目(时间和/或内存消耗)然后a 3*O(n)迭代......所以,最后正确的答案总是一样的:它取决于.当您不能或不想重建列表时,这只是另一种方法. (7认同)
  • 注意:根据MSDN,"ObservableCollection.IndexOf"方法是"一个O(n)操作,其中n是Count",我怀疑"ObservableCollection.Move"方法调用"ObservableCollection.RemoveAt"和"ObservableCollection.Insert"方法中的每一个(同样,根据MSDN)也是"O(n)操作,其中n是计数".接下来的评论...... (3认同)
  • *如果*运行时很重要,我怀疑您最好使用排序后的列表(即,使用“ ObservableCollection &lt;T&gt;(List &lt;T&gt;)”构造函数)重新创建“ ObservableCollection”。正如我在上面的“ D_Learning”对“ Sergey Brunov”的评论中所评论的:“如果您(例如,使用MVVM模式时,您将使用)属性(在实现“ INotifyPropertyChanged”接口的类中) ),将“ ObservableCollection”设置为新的排序版本,然后Setter调用接口的“ PropertyChanged”事件,则绑定不会中断。” (2认同)
  • 我对此强调不足:** CollectionViewSource中没有未来**。UWP不支持它,如果您现在依赖它,则将被过时的设计所困扰。在这个确切的问题上已经花光了,如果您想在将来某天将代码移植到Windows 10,请坚持使用** ObservableCollection **。 (2认同)

Con*_*ngo 12

这种扩展方法消除了对整个列表进行排序的需要。

相反,它会将每个新项目插入到位。所以列表总是保持排序。

事实证明,当集合更改时由于缺少通知而导致许多其他方法失败时,此方法才有效。而且速度相当快。

下面的代码应该是防弹的;它已经在大规模生产环境中进行了广泛的测试。

使用:

// 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.
Run Code Online (Sandbox Code Playgroud)

和扩展方法:

/// <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);
}
Run Code Online (Sandbox Code Playgroud)

和二进制搜索扩展方法:

/// <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;
}
Run Code Online (Sandbox Code Playgroud)


Lan*_*nce 10

我为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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

它似乎工作,你不需要实现IComparable

  • 虽然这似乎比移动所有项目要快得多,但它也会触发不同的事件(删除和添加而不是移动)。根据用例,这可能不是一个问题,但绝对是需要记住的事情。 (2认同)

小智 7

我看了看这些,然后对其进行了排序,然后像上面那样打破了绑定。提出了这个解决方案,尽管它比大多数解决方案都简单,但它似乎可以满足我的要求,

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;



    }
Run Code Online (Sandbox Code Playgroud)

  • 虽然这似乎比移动所有项目快得多,但它也会触发不同的事件(删除和添加而不是移动)。根据用例,这可能不是问题,但绝对是要记住的事情。 (3认同)
  • @DonaldAirey 我说的是事件是通过不同的事件参数内容触发的。根据事件处理程序正在侦听的内容,这可能会导致一些问题。 (2认同)

Shi*_*mmy 7

这是一个ObservableCollection<T>,可根据更改自动对自身进行排序,仅在必要时触发排序,并且仅触发单个move collection更改操作。

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 (SortingSelector == null 
          || e.Action == NotifyCollectionChangedAction.Remove
          || e.Action == NotifyCollectionChangedAction.Reset)
        return;

      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}, ");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

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,
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,实时效果很好!评论很少:降序反转(需要修复);不想为元组安装 Nuget 的人只需替换“(项目:项目,索引:索引));” -&gt;“新{索引=索引,项目=项目});” 和 "(OldIndex: tuple.Index, NewIndex: index))" -&gt; "new { OldIndex = tuple.Index, NewIndex = index })"。 (2认同)