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


97

我有以下课程:

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

问:如何按键排序?


您是在类中寻找排序实现还是只是任何类型的排序都可以?
okw

不确定如何理解。基本上我只是想对它进行排序,所以收藏不会很大(最多20个项目),所以任何事情都可以做(很有可能)
Maciek 2009年

一个WPF的解决方案请参见本stackoverflow.com/questions/1945461/...
Gayot酒店FOW

查看此页面上的答案:当一些重要的基本功能需要22个以上的答案时,API会非常清楚地指示损坏的API。
格里

Answers:


19

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

我已经更新了代码,以提高性能并处理重复项(感谢nawfal突出显示了原始数据的不良性能,即使它在原始数据示例中运行良好)。可观察对象分为左排序的一半和右未排序的一半,其中每次最小项(在排序列表中找到)都从未排序移到排序分区的末尾。最差情况O(n)。本质上是一种选择排序(有关输出,请参见下文)。

public static void Sort<T>(this ObservableCollection<T> collection)
        where T : IComparable<T>, IEquatable<T>
    {
        List<T> sorted = collection.OrderBy(x => x).ToList();

        int ptr = 0;
        while (ptr < sorted.Count - 1)
        {
            if (!collection[ptr].Equals(sorted[ptr]))
            {
                int idx = search(collection, ptr+1, sorted[ptr]);
                collection.Move(idx, ptr);
            }
            
            ptr++;
        }
    }

    public static int search<T>(ObservableCollection<T> collection, int startIndex, T other)
            {
                for (int i = startIndex; i < collection.Count; i++)
                {
                    if (other.Equals(collection[i]))
                        return i;
                }
    
                return -1; // decide how to handle error case
            }

用法:与观察者一起采样(使用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 = "Alice", Age = 39},
                    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 = 5},
                    new Person {Name = "Kim", Age = 19}
                };
    
                //what do observers see?
            
    
observable.CollectionChanged += (sender, e) =>
        {
            Console.WriteLine(
                e.OldItems[0] + " move from " + e.OldStartingIndex + " to " + e.NewStartingIndex);
            int i = 0;
            foreach (var person in sender as ObservableCollection<Person>)
            {
                if (i == e.NewStartingIndex)
                {
                    Console.Write("(" + (person as Person).Age + "),");
                }
                else
                {
                    Console.Write((person as Person).Age + ",");
                }
                
                i++;
            }

            Console.WriteLine();
        };

排序进度的详细信息显示了集合是如何旋转的:

Sue aged 5 move from 8 to 0
(5),51,12,13,39,14,41,20,39,19,
Jack aged 12 move from 2 to 1
5,(12),51,13,39,14,41,20,39,19,
Bob aged 13 move from 3 to 2
5,12,(13),51,39,14,41,20,39,19,
John aged 14 move from 5 to 3
5,12,13,(14),51,39,41,20,39,19,
Kim aged 19 move from 9 to 4
5,12,13,14,(19),51,39,41,20,39,
Jane aged 20 move from 8 to 5
5,12,13,14,19,(20),51,39,41,39,
Alice aged 39 move from 7 to 6
5,12,13,14,19,20,(39),51,41,39,
Jim aged 39 move from 9 to 7
5,12,13,14,19,20,39,(39),51,41,
Mary aged 41 move from 9 to 8
5,12,13,14,19,20,39,39,(41),51,

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

  • EDIT对同一集合排序而不创建新副本*

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

****原始答案-这将创建一个新集合****可以使用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;
    }
}

发现了这个,对它最有帮助。是由LINQ组成sortedOC变量吗?
Jason94 '04 -4-26

9
不喜欢这个答案,因为它没有给您排序的ObservableCollection。
xr280xr 2012年

63
-1,因为它不排序 ObservableCollection,而是创建一个新的集合。
科斯2012年

2
更新的代码可以使用,但是时间复杂度为O(n ^ 2)。通过使用BinarySearch代替可以将其改进为O(n * log(n))IndexOf
威廉·莫里森

2
优秀的解决方案!对于那些继承自ObservableCollection <T>的方法,可以使用受保护的MoveItem()方法代替使用RemoveAt和Insert方法。参见:referencesource.microsoft.com/#system/compmod/system/...
赫尔曼·科德斯

84

这个简单的扩展程序对我来说效果很好。我只需要确保MyObjectIComparable。在的可观察集合上调用sort方法时,将调用on 方法MyObjects,该CompareTo方法MyObject将调用我的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();

7
这应该是答案
thumbmunkeys 2013年

1
更新了我在上面的答案,因为它是被接受的,并且针对该答案进行了性能改进,从而提高了收藏集中所有内容的更改通知
Andrew

3
好答案。您为什么使用return Utils.LogicalStringCompare(a.Title, b.Title);而不是return string.Compare(a.Title, b.Title);?@NeilW

2
@Joe,我需要进行逻辑比较而不是标准字符串比较,这就是为什么我需要首先编写扩展名的原因。逻辑字符串比较会正确地对字符串中的数字进行排序,而不是像字符串一样对它们进行排序(
1、2、20、1000

4
这绝对是要走的路。我添加了自己的扩展答案,允许您传递keySelector而不是像LINQ那样使用IComparable。
Jonesopolis

39

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

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

更新

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

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

但是请注意备注

由于所涉及接口的相对复杂性及其相对较差的文档(请参阅https://stackoverflow.com/a/5883947/33080),可能会带来问题。


2
实际上,此博客更有用。但是,对于具有可观察的集合并在添加和删除项目时保持其排序的问题,我尚未找到一个不错的答案。我要写我自己的想法。
斯蒂芬·德鲁

@Steve您可以尝试这个
罗曼·斯塔科夫

感谢您的链接,我选择了扩展方法,因为这似乎是最简洁的解决方案。
发挥

bw有人注意到博客在html文件名(obversablecollection)中有错字吗?:P
laishiekai

1
@romkyns答案是扩展ObservableCollection <T>。GridView可以识别它。然后,就像您一样,隐藏它的方法。有空的时候我会发布完整的解决方案。
2013年

25

您可以使用以下简单方法:

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

您可以这样排序:

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

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


4
这将清除ObservableCollection,然后重新添加所有对象-因此,值得注意的是,如果将UI绑定到集合,则不会看到动画更改,例如,当项目移动时
Carlos P

1
我不确定为什么必须显示移动的项目...例如,您通常ObservableCollection绑定到下拉菜单的ItemSource,而根本看不到该集合。清除和填充的操作也非常快...“慢”的可以是已经优化的排序。最后,您可以修改此代码以实现您的move方法,sortedlistsource其余的操作很容易。
贾德

3
如果您一定要下拉菜单,那么看到物品四处走动不会从中受益,这是事实。如果绑定到ListBox,则在重新索引集合中的对象时,WPF或Silverlight或Windows Store Apps之类的框架将提供有用的视觉反馈。
卡洛斯·P

尽管这比“移动”方法快,但会引发许多“重置/添加”事件。投票率最高的答案(移动方法)使此问题最小化,并正确引发Move事件,这也仅适用于真正感动的事件。
nawfal

19

WPF使用ListCollectionView该类开箱用地进行实时排序

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

初始化完成后,便无事可做。与被动排序相比,优点是ListCollectionView以对开发人员透明的方式完成了所有繁重的工作。新项目将自动按照正确的排序顺序放置。从IComparerT 派生的任何类都适用于自定义sort属性。

有关文档和其他功能,请参见ListCollectionView


6
实际上有效的:D,比起这种简单任务的其他“过度设计”的解决方案来说,是一种更好的解决方案。
MushyPeas

您的博客去了哪里?
phoog

“透明”事物的问题是,当它不起作用时,您将看不到应该看的地方。微软的文档中有一个100%透明的示例,即您根本看不到它。
保罗·麦卡锡

15

我喜欢上面“ 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);
                }
            }
        }
    }

调用方式与调用OrderBy的方式相同,只不过它将对ObservableCollection的现有实例进行排序而不是返回新的collection:

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

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

1
感谢您发布此代码-正如Richie博客评论中所指出的,此代码有一些有价值的增强;特别是使用来源的“移动”方法。我想这会用source.Move(j-1,j);代替Remove / Insert行。
卡洛斯·P


@Jaider是的,它已经过优化,只是没有提高整体原始速度。
jv42 2014年

这会引发大量的Remove / Add事件(对于我相信的每一个N)。投票最高的答案将其最小化,并正确地引发Move事件,这也仅适用于真正感动的事件。这里的关键是不要立即进行就地排序,而是使用外部进行排序OrderBy,然后进行比较以找出实际变化。
nawfal

11

@NielW的答案是进行真正就地排序的方法。我想添加一个稍微改变的解决方案,使您可以不必使用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);

2
对于额外的巧克力曲奇,您可以if(!Ascending) sorted.Reverse();for:D 之前添加一个布尔参数“ Ascending”和一个(不需要再担心内存,Reverse方法不会创建任何新对象,它就地是反向的)
Sharky 2015年

根据我的测试collection.Move(0,0)导致CollectionChanged事件。因此,首先检查是否需要移动将是一项性能改进。
sa.he

10

我想补充一下NeilW的答案。合并类似于orderby的方法。将此方法添加为扩展:

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

8

变体是您使用选择排序算法对集合进行排序的位置。使用该Move方法将元素移动到位。每一步都会使用触发CollectionChanged事件NotifyCollectionChangedAction.Move(也PropertyChanged使用属性名Item[])。

该算法具有一些不错的属性:

  • 该算法可以实现为稳定排序。
  • 集合中移动的项目数(例如,CollectionChanged触发的事件)几乎总是少于其他类似的算法,例如插入排序和冒泡排序。

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

这是一种扩展方法,为简单起见,需要元素实现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);
    }
  }

}

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

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

1
这是这里所描述的所有我的首选排序的方式,不幸的是,Move方法不可用在Silverlight 5
爱德华多Brites

1
我收到错误消息'Profiler.Profile.ProfileObject'不能在通用类型或方法'ObservableCollectionExtensions.Sort <T>(ObservableCollection <T>)'中用作类型参数'T'。没有从'Profiler.Profile.ProfileObject'到'System.IComparable <Profiler.Profile.ProfileObject>的隐式引用转换
New Bee

1
@NewBee:此扩展方法指定一个通用约束T以能够对集合中的元素进行排序。排序涉及大于和小于的概念,只有您才能定义ProfileObject排序方式。要使用你需要实现扩展方法IComparable<ProfileObject>ProfileObject。如指出的,其他替代方法指定a IComparer<ProfileObject>或a Func<ProfileObject, ProfileObject, int>并相应地更改排序代码。
Martin Liversage

4

为了稍微改善xr280xr答案的扩展方法,我添加了一个可选的bool参数,以确定排序是否降序。我也将卡洛斯·P(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);
            }
        }
    }

2

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

如果您需要始终对集合进行排序,即使您插入或删除元素并且插入速度也不成问题,那么您也可以实现SortedObservableCollection提到的@Gerrie Schenck 之类的方法或查看此实现

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

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

这将需要一些时间来对集合进行排序,但是即使如此,它也可能是最好的解决方案,具体取决于您对它的处理方式。


1
此答案中的链接指向LGPL许可的代码,因此,如果您是Silverlight(无法动态链接)或不开源,请谨慎使用该代码。
yzorg 2011年

2

我当前的答案已经获得了最多的选票,但是我发现了一种更好,更现代的方法。

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

更新原始答案会更好吗?
内森·休斯

不。它已经比其他任何答案都更受好评。我不会以为人们会喜欢这样做。只是以为我会提供另一种方法,特别是因为有新的悬赏悬赏。
NielW


1

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

(未试)

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


1

哎呀,我也将给出一个快速拼凑在一起的答案……看起来有点像这里的其他一些实现,但我会添加它:

(经过严格测试,希望我不会感到尴尬)

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

1)必须分类ObservableCollection<T>到位,以维护通知等。

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

1

在我的情况下,这些答案均无效。要么是因为它搞砸了装订,要么是因为需要太多额外的编码,简直就是一场噩梦,或者答案被打破了。因此,这是我认为的另一个简单答案。它的代码要少得多,并且通过额外的this.sort类型的方法可以保持相同的可观察集合。让我知道是否有某些原因我不应该这样做(效率等)?

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是我的公开课。只是看起来简单得多。增加的好处:它实际上有效,并且不会与绑定混淆或返回新的集合等。


1

好吧,由于我在使ObservableSortedList与XAML一起使用时遇到问题,因此我继续创建了SortingObservableCollection。它继承自ObservableCollection,因此可以与XAML一起使用,并且已经对其进行了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,因此它应可与Windows Store,Windows Phone和.NET 4.5.1一起使用。


1
您可能不应该new在所有这些方法上使用,如果某人具有更通用类型的实例,则不会调用这些方法。而是override每种可重写的方法,并根据需要更改它们,或回退到base.Method(...)。例如,您甚至不必担心,.Add因为它在内部使用.InsertItem,因此,如果对其.InsertItem进行了覆盖和调整,.Add则不会影响订购。
HB

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

1

这对我有用,很久以前就在某个地方找到它。

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

0

我需要能够按多种方式分类,而不仅仅是一种。该答案基于其他一些答案,但可以进行更复杂的排序。

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

0

我从其他解决方案中学到了很多,但是发现了两个问题。首先,有些依赖于IndexOf,对于大型列表,它往往非常慢。其次,我的ObservableCollection具有EF实体,使用Remove似乎破坏了某些外键属性。也许我做错了。

无论如何,可以使用“移动”代替“删除/插入”,但这会导致性能修复出现一些问题。

为了解决性能问题,我创建了一个具有IndexOf排序值的字典。为了使字典保持最新状态并保留实体属性,请使用通过两次移动实现的交换,而不是在其他解决方案中实现的一次交换。

单步移动在位置之间移动元素的索引,这将使IndexOf字典无效。添加第二步以实现交换可恢复位置。

public static void Sort<TSource, TKey>(this ObservableCollection<TSource> collection, Func<TSource, TKey> keySelector)
{
    List<TSource> sorted = collection.OrderBy(keySelector).ToList();
    Dictionary<TSource, int> indexOf = new Dictionary<TSource, int>();

    for (int i = 0; i < sorted.Count; i++)
        indexOf[sorted[i]] = i;

    int idx = 0;
    while (idx < sorted.Count)
        if (!collection[idx].Equals(sorted[idx])) {
            int newIdx = indexOf[collection[idx]]; // where should current item go?
            collection.Move(newIdx, idx); // move whatever's there to current location
            collection.Move(idx + 1, newIdx); // move current item to proper location
        }
        else {
            idx++;
        }
}

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

哦,我明白了……Gayot希望将悬赏金提供给最
不满意的

没看过讽刺
奖赏
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.