C# 如何对可观察集合进行排序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1945461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:06:33  来源:igfitidea点击:

How do I sort an observable collection?

c#.netwpfsortingobservablecollection

提问by Maciek

I have a following class :

我有以下课程:

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

Which I've put in an ObservableCollection :

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

Q : How do I sort it by key ?

问:如何按关键字排序?

采纳答案by Andrew

** OP EDIT ** Subsequently revised to show sorting original collection without creating a new one. (Original answer can be found at the end)

** OP EDIT ** 随后修改以显示排序原始集合而不创建新集合。(原文答案见文末)

Sorting an observable and returning the same object sorted can be done using an extension method. For larger collections watch out for the number of collection changed notifications e.g.

可以使用扩展方法对 observable 进行排序并返回已排序的相同对象。对于较大的收藏,请注意收藏更改通知的数量,例如

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

usage: Sample with an observer (used a Person class to keep it simple)

用法:带有观察者的样本(使用 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 = "Hyman", 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();
    };

Output from above:
removed Katy aged 51 at index 0
added Katy aged 51 at index 8
removed Mary aged 41 at index 3
added Mary aged 41 at index 7
removed Jane aged 20 at index 3
added Jane aged 20 at index 5
removed Jim aged 39 at index 3
added Jim aged 39 at index 6
removed Jane aged 20 at index 4
added Jane aged 20 at index 5

上面的输出:
删除索引 0 时
51 岁的凯蒂 添加索引 8 时 51 岁的凯蒂
删除索引 3 时
41 岁的玛丽 添加索引 7 时 41 岁的玛丽
删除索引 3 时
20 岁的 Jane 添加索引 5 时 20 岁的 Jane
删除索引39 时的吉姆在索引 3
添加 Jim 39 岁在索引 6
删除 Jane 20 岁在索引 4
添加 Jane 20 岁在索引 5

The Person class implements both IComparable and IEquatable the latter is used to minimise the changes to the collection so as to reduce the number of change notifications raised

Person 类同时实现了 IComparable 和 IEquatable,后者用于最小化对集合的更改,从而减少引发的更改通知的数量

  • EDIT Sorts same collection without creating a new copy *
  • 编辑对同一集合进行排序而不创建新副本 *

To return an ObservableCollection, call .ToObservableCollection on *sortedOC* using e.g. [this implementation][1].

要返回 ObservableCollection,请使用例如 [this implementation][1] 在 *sortedOC* 上调用 .ToObservableCollection。

**** orig answer **** You can use linq as the doSort method below illustrates. A quick code snippet: produces

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

3:xey 6:fty 7:aaa

3:xey 6:fty 7:aaa

Alternatively you could use an extension method on the collection itself

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

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

回答by Gerrie Schenck

Make a new class SortedObservableCollection, derive it from ObservableCollectionand implement IComparable<Pair<ushort, string>>.

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

回答by bruno conde

Do you need to keep your collection sorted at all times? When retrieving the pairs, do you need them to be always sorted, or it's only for a few times (maybe just for presenting)? How big do you expect your collection to be? There are a lot of factors that can help you decide witch method to use.

您是否需要随时整理您的收藏?检索对时,您是否需要始终对它们进行排序,还是仅排序几次(可能只是为了展示)?您希望您的收藏有多大?有很多因素可以帮助您决定要使用的女巫方法。

If you need the collection to be sorted at all times, even when you insert or delete elements and insertion speed is not a problem maybe you should implement some kind of SortedObservableCollectionlike @Gerrie Schenck mentioned or check out this implementation.

如果您需要始终对集合进行排序,即使您插入或删除元素并且插入速度不是问题,也许您应该实现某种SortedObservableCollection类似@Gerrie Schenck 提到的或查看此实现

If you need your collection sorted just for a few times use:

如果您需要对您的收藏进行几次排序,请使用:

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

This will take some time to sort the collection, but even so, it might be the best solution depending on what your doing with it.

这将需要一些时间来对集合进行排序,但即便如此,它也可能是最佳解决方案,具体取决于您使用它做什么。

回答by Adam Ralph

One way would be to convert it to a List and then call Sort(), providing a comparison delegate. Something like:-

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

(untested)

(未经测试)

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

回答by Eric J.

I found a relevant blog entry that provides a better answer than the ones here:

我找到了一个相关的博客条目,它提供了比这里更好的答案:

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

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

UPDATE

更新

The ObservableSortedListthat @romkyns points out in the comments automatically maintains sort order.

@romkyns 在评论中指出的ObservableSortedList会自动维护排序顺序。

Implements an observable collection which maintains its items in sorted order. In particular, changes to item properties that result in order changes are handled correctly.

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

However note also the remark

但是请注意备注

May be buggy due to the comparative complexity of the interface involved and its relatively poor documentation (see https://stackoverflow.com/a/5883947/33080).

由于所涉及的界面相对复杂且文档相对较差,因此可能存在问题(请参阅https://stackoverflow.com/a/5883947/33080)。

回答by Jaider

You can use this simple method:

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

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

You can sort like this:

你可以这样排序:

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

More detail: http://jaider.net/2011-05-04/sort-a-observablecollection/

更多细节:http: //jaider.net/2011-05-04/sort-a-observablecollection/

回答by xr280xr

I liked the bubble sort extension method approach on "Richie"'s blog above, but I don't necessarily want to just sort comparing the entire object. I more often want to sort on a specific property of the object. So I modified it to accept a key selector the way OrderBy does so you can choose which property to sort on:

我喜欢上面“Richie”博客上的冒泡排序扩展方法方法,但我不一定只想对整个对象进行排序比较。我更经常想对对象的特定属性进行排序。因此,我将其修改为以 OrderBy 的方式接受键选择器,以便您可以选择要排序的属性:

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

Which you would call the same way you would call OrderBy except it will sort the existing instance of your ObservableCollection instead of returning a new collection:

您可以像调用 OrderBy 一样调用它,除了它会对 ObservableCollection 的现有实例进行排序,而不是返回一个新集合:

ObservableCollection<Person> people = new ObservableCollection<Person>();
...

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

回答by Jonathan Morales Vélez

To improve a little bit the extension method on xr280xr answer I added an optional bool parameter to determine whether the sorting is descending or not. I also included the suggestion made by Carlos P in the comment to that answer. Please see below.

为了稍微改进 xr280xr 答案的扩展方法,我添加了一个可选的 bool 参数来确定排序是否为降序。我还在对该答案的评论中包含了 Carlos P 提出的建议。请参阅下文。

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

回答by Martin Liversage

A variation is where you sort the collection in place using a selection sortalgorithm. Elements are moved into place using the Movemethod. Each move will fire the CollectionChangedevent with NotifyCollectionChangedAction.Move(and also PropertyChangedwith property name Item[]).

一种变体是使用选择排序算法对集合进行原地排序。使用该Move方法将元素移动到位。每次移动都会CollectionChanged使用NotifyCollectionChangedAction.Move(以及PropertyChanged属性名称Item[])触发事件。

This algorithm has some nice properties:

这个算法有一些很好的特性:

  • The algorithm can be implemented as a stable sort.
  • The number of items moved in the collection (e.g. CollectionChangedevents fired) is almost always less than other similar algorithms like insertion sort and bubble sort.
  • 该算法可以实现为稳定排序。
  • 集合中移动的项目数(例如CollectionChanged触发的事件)几乎总是少于其他类似的算法,如插入排序和冒泡排序。

The algorithm is quite simple. The collection is iterated to find the smallest element which is then moved to the start of the collection. The process is repeated starting at the second element and so on until all elements have been moved into place. The algorithm is not terribly efficient but for anything you are going to display in a user interface it shouldn't matter. However, in terms of the number of move operations it is quite efficient.

算法非常简单。迭代集合以找到最小的元素,然后将其移动到集合的开头。从第二个元素开始重复该过程,依此类推,直到所有元素都移动到位。该算法并不是非常有效,但对于您要在用户界面中显示的任何内容,它都无关紧要。但是,就移动操作的数量而言,它非常有效。

Here is an extension method that for simplicity requires that the elements implement IComparable<T>. Other options are using an IComparer<T>or a Func<T, T, Int32>.

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

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 a collection is simply a matter of invoking the extension method:

对集合进行排序只是调用扩展方法的问题:

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

回答by Rex

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