以下にObservableCollection<string>
があります。 sortこれをアルファベット順にする必要があります。
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_?>);
これどうやってするの?
基本的に、ソートされたコレクションを表示する必要がある場合は、CollectionViewSource
クラスの使用を検討してください。ソースコレクション(ObservableCollection<T>
クラスのインスタンス)にSource
プロパティを割り当て( "バインド")してください。
アイデアは、 CollectionViewSource
クラスがCollectionView
クラスのインスタンスを提供する ということです。これは、元の(ソース)コレクションの一種の「投影」ですが、並べ替え、フィルタリングなどが適用されています。
参照:
WPF 4.5では、CollectionViewSource
の「ライブシェーピング」機能が導入されています。
参照:
ObservableCollection<T>
クラスのインスタンスをソートする必要がある場合は、次のようにします。 ObservableCollection<T>
クラス自体にはsortメソッドがありません。ただし、コレクションを再作成して、アイテムを並べ替えることができます。
// 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()
およびOrderByDescending()
メソッド(他のLINQ拡張メソッド)は、ソースコレクションを変更しないことに注意してください。代わりに新しいシーケンスを作成します(つまり、IEnumerable<T>
インターフェイスを実装するクラスの新しいインスタンス)。したがって、コレクションを再作成する必要があります。
私はこれが古い質問であることを知っていますが、「sort observablecollection」の最初のグーグルの結果なので、私の2セントを残す価値があると考えました。
私が行く方法は、List<>
から始まるObservableCollection<>
を構築し、それを(Sort()
メソッドで msdnの詳細 で)ソートし、List<>
がソートされました。Move()
メソッドでObservableCollection<>
を並べ替えます。
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);
}
}
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]);
}
}
これは単なる概念実証であり、アイテムのバインディングを壊さずにObservableCollection<>
をソートする方法を示しています。ソートアルゴリズムには、改善点と検証の余地があります(指摘されたインデックスチェックなど here ) 。
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を実装する必要はありません
私はこれらを見て、それをソートしていましたが、上記のようにバインディングを破りました。この解決策に気付いたのは、あなたのほとんどよりも簡単ですが、私がしたいことをするようです、
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;
}
これは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 (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}, ");
}
}
}
出力:
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,
OrderByDescending
への引数は、ソートするキーを返す関数です。あなたの場合、キーは文字列そのものです:
var result = _animals.OrderByDescending(a => a);
たとえば、長さでソートする場合は、次のように記述します。
var result = _animals.OrderByDescending(a => a.Length);
_animals.OrderByDescending(a => a.<what_is_here_?>);
動物がオブジェクトAnimalのリストである場合、プロパティを使用してリストを並べ替えることができます。
public class Animal
{
public int ID {get; set;}
public string Name {get; set;}
...
}
ObservableCollection<Animal> animals = ...
animals = animals.OrderByDescending(a => a.Name);
myObservableCollection.ToList().Sort((x, y) => x.Property.CompareTo(y.Property));
この拡張方法により、リスト全体をソートする必要がなくなります。
代わりに、それぞれの新しいアイテムを所定の位置に挿入します。
したがって、リストは常にソートされたままです。
このメソッドは、コレクションが変更されたときに通知が欠落しているために他の多くのメソッドが失敗した場合にのみ機能することがわかりました。そして、それはかなり速いです。
使用するには:
// 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;
}
/// <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);
}
}
このソリューションは、 マルコの答え に基づいています。私は いくつかの問題 が彼のソリューションにあったため、インデックスが実際に変更された場合にのみMove
を呼び出すことで改善しました。これにより、パフォーマンスが向上し、リンクされた問題も修正されます。
すでによく知られている IComparable<T>
interface を既に実装しているクラスのコレクションについて、Shimmyのわずかなバリエーションを次に示します。この場合、「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));
}
}
特定のクラスフィールド(距離)で並べ替えを行いました。
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);
}