監視可能なコレクションを並べ替えるにはどうすればよいですか?


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

Q:キーで並べ替えるにはどうすればよいですか?


クラス内の並べ替えの実装を探していますか、それとも任意の種類の並べ替えで十分ですか?
okw

それを理解する方法がわかりません。基本的に私はそれをソートしたいだけです、コレクションはあまり大きくないので(最大20アイテム)、何でもできるでしょう(おそらく)
Maciek

WPFのソリューションのためにこれを参照してくださいstackoverflow.com/questions/1945461/...
Gayot FOW

このページの回答をご覧ください。重要で基本的な機能に22以上の回答が必要な場合、APIが壊れていることを非常に明確に示しています。
ジェリー

回答:


19

オブザーバブルを並べ替え、同じオブジェクトを並べ替えて返すには、拡張メソッドを使用します。大きなコレクションの場合は、コレクション変更通知の数に注意してください。

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

上からの出力:
51歳のケイティをインデックス0 から削除
51歳のケイティを
削除インデックス8の51歳のケイティを削除インデックス3の
41歳のメアリーを
削除インデックス7の41歳のメアリーを削除インデックス3の
20歳のジェーンを
削除インデックス5の20歳のジェーンを追加インデックス5の20歳のジェーンを削除ジムの39歳を削除インデックス3で
追加された39歳のジムインデックス6で
削除された20歳のジェーンインデックス4で
追加された20歳のジェーンインデックス5で追加されたジェーン

PersonクラスはIComparableとIEquatableの両方を実装します。後者は、コレクションへの変更を最小限に抑え、発生する変更通知の数を減らすために使用されます

  • EDIT新しいコピーを作成せずに同じコレクションをソートします*

ObservableCollectionを返すには、たとえば[この実装] [1]を使用して* sortedOC *で.ToObservableCollectionを呼び出します。

****元の回答-これにより新しいコレクションが作成されます****以下のdoSortメソッドが示すようにlinqを使用できます。簡単なコードスニペット:生成

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

これを見つけて、それが最も役に立ちます。sortedOC変数を構成するのはLINQですか?
Jason94 2012

9
これはソートされたObservableCollectionを提供しないため、この回答のファンではありません。
xr280xr

63
-1はをソートしないため ObservableCollection、代わりに新しいコレクションを作成します。
コス

2
更新されたコードは機能しますが、O(n ^ 2)時間複雑です。これは、のBinarySearch代わりにを使用してO(n * log(n))に改善できますIndexOf
ウィリアムモリソン

2
優れたソリューション!ObservableCollection <T>から継承するものについては、RemoveAtメソッドとInsertメソッドを使用する代わりに、保護されたMoveItem()メソッドを使用することが可能です。参照:referencesource.microsoft.com/#system/compmod/system/...
ハーマンコルド

84

この単純な拡張は、私にとっては美しく機能しました。私はそれがそうであることを確認しなければならなかっMyObjectIComparable。ソート方法は、観察のコレクションで呼び出された場合にはMyObjectsCompareTo上の方法は、MyObject私の論理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
Joe

2
@Joe、標準の文字列比較ではなく論理比較を行う必要があったため、最初に拡張機能を作成する必要がありました。論理文字列比較は、文字列のように順序付けするのではなく、文字列の数値を適切に並べ替えます(
1、1000、2、20

4
これは絶対に進むべき道です。これを拡張した独自の回答を追加しました。これにより、LINQが通常行うように、IComparableを使用する代わりにkeySelectorを渡すことができます。
ジョネソポリス2015

39

私はここのものよりも良い答えを提供する関連するブログエントリを見つけました:

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

更新

ObservableSortedList @romkynsが自動的にコメントで指摘していることは、ソート順序を維持します。

項目をソートされた順序で維持する監視可能なコレクションを実装します。特に、順序の変更につながるアイテムプロパティの変更は正しく処理されます。

ただし、備考にも注意してください

関連するインターフェースが比較的複雑でドキュメントが比較的貧弱であるため、バグがある可能性があります(https://stackoverflow.com/a/5883947/33080を参照)。


2
確かに、このブログはもっと便利です。しかし、アイテムが追加されたり削除されたりしてもソートを維持する監視可能なコレクションを持つという質問に対する適切な答えはまだ見つかりません。自分で書こうと思います。
Stephen Drew

@Steveあなたはこれを試すことができます。
Roman Starkov

リンクをありがとう、これは整然とした解決策のようだったので、拡張メソッドを使用しました。魅力的な作品:D
pengibot '20

bw誰かがブログのhtmlファイル名に誤植(obversablecollection)があることに気づきましたか?:P
laishiekai

1
@romkyns答えはObservableCollection <T>を拡張することでした。GridViewはそれを問題なく認識します。次に、メソッドと同じように非表示にします。時間があれば、完全なソリューションを投稿します。
Weston

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にバインドしていて、コレクションがまったく表示されません。また、このクリアとフィルアップの操作は超高速です。「遅い」操作は、すでに最適化されている種類の場合があります。最後に、あなたは、あなたの移動の方法を実装するには、このコードを変更することができたsortedlistし、source残りは簡単です。
Jaider

3
ドロップダウンにバインドされている場合は、アイテムが動き回るのを見てもメリットはありません。ただし、ListBoxにバインドされている場合、WPF、Silverlight、またはWindowsストアアプリなどのフレームワークは、コレクション内のオブジェクトのインデックスが再作成されるときに有用な視覚的フィードバックを提供します。
Carlos P

これは移動アプローチよりも高速ですが、多くのリセット/追加イベントが発生します。最も投票数の多い回答(移動アプローチ)はこれを最小化し、Moveイベントを正しく発生させます。これも、本当に移動したイベントにのみ発生します。
nawfal

18

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、カスタムソートプロパティに適しています。

ドキュメントとその他の機能については、ListCollectionViewを参照してください。


6
それは実際に機能しました:Dこれは、このような単純なタスクのための他の「オーバーエンジニアリング」ソリューションよりも優れたソリューションです。
MushyPeas

あなたのブログはどこに行きましたか?
phoog 2018

「透明な」ものの問題は、それが機能しないときにどこを見ればよいかわからないことです。Microsoftのドキュメントには10​​0%透過的な例があります。つまり、まったく見ることができません。
ポールマッカーシー

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

新しいコレクションを返す代わりにObservableCollectionの既存のインスタンスをソートすることを除いて、OrderByを呼び出すのと同じ方法で呼び出します。

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

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

1
これを投稿していただきありがとうございます。Richieのブログのコメントで指摘されているように、このコードにはいくつかの価値のある拡張機能があります。特に、ソースの「移動」メソッドを使用します。これは、Remove / Insert行をsource.Move(j-1、j);に置き換えると思います。
Carlos P

2
このソートアルゴリズムは最適化されていませんen.wikipedia.org/wiki/Sorting_algorithm
Jaider

@ジェイダーはい、それは最適化されていますが、全体的な生の速度ではありません。
jv42 14

これはいくつかの削除/追加イベントを発生させます(私は信じているすべてのNについて)。ここで重要なのは、すぐにインプレースソートを実行するのではなく、外部でソート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
チョコレートクッキーを追加する場合は、ブールパラメータ「昇順」と:Dのif(!Ascending) sorted.Reverse();直前を追加できますfor(さらに、メモリについて心配する必要はありません。Reverseメソッドは新しいオブジェクトを作成せず、インプレースリバースです)
Sharky

私のテストによれば、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.MovePropertyChangedプロパティ名も発生しますItem[])。

このアルゴリズムには、いくつかの優れた特性があります。

  • アルゴリズムは、安定したソートとして実装できます。
  • コレクション内で移動されるアイテムの数(CollectionChanged発生するイベントなど)は、ほとんどの場合、挿入ソートやバブルソートなどの他の類似のアルゴリズムよりも少なくなります。

アルゴリズムは非常に単純です。コレクションが反復されて、最小の要素が検出され、その後、コレクションの先頭に移動されます。このプロセスは、すべての要素が所定の位置に移動されるまで、2番目の要素から開始して繰り返されます。アルゴリズムはそれほど効率的ではありませんが、ユーザーインターフェイスに表示するものは何でもかまいません。ただし、移動操作の数に関しては、非常に効率的です。

単純化のために要素がを実装する必要がある拡張メソッドを次に示します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。その他の選択肢は、IComparer<ProfileObject>aまたはa Func<ProfileObject, ProfileObject, int>を指定し、それに応じて並べ替えコードを変更するように記載されています。
Martin Liversage 2017年

4

xr280xr回答の拡張メソッドを少し改善するために、オプションのboolパラメーターを追加して、ソートが降順かどうかを判断しました。その回答のコメントには、カルロス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

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

元の答えを更新した方がいいのではないですか?
Nathan Hughes

いいえ。それは他のどの回答よりもすでに高く評価されています。私は人々がこのようにそれをすることを望んでいると思いません。特に新しい答えに恵みがあったため、別の方法を提案すると思いました。
NielW 2016

1

新しいクラスを作成しSortedObservableCollection、それから派生してObservableCollection実装しIComparable<Pair<ushort, string>>ます。


1

1つの方法は、リストに変換してから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ストア、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

私は、1つだけでなく、複数のものでソートできるようにする必要がありました。この回答は他の回答のいくつかに基づいていますが、より複雑なソートが可能です。

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でソートされた値を使用して辞書を作成します。辞書を最新の状態に保ち、エンティティのプロパティを保持するには、他のソリューションで実装されている1つの移動ではなく、2つの移動で実装されたスワップを使用します。

1回の移動で要素のインデックスが場所間で移動し、IndexOf辞書が無効になります。スワップを実装するための2番目の移動を追加すると、場所が復元されます。

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は、最も否定的な回答に賞金を与えたかったのですlol
NielW

皮肉で恵み:)授与見たことがない
nawfal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.