Как отсортировать наблюдаемую коллекцию? - PullRequest
93 голосов
/ 22 декабря 2009

У меня есть следующий класс:

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

В: Как мне отсортировать его по ключу?

Ответы [ 22 ]

2 голосов
/ 07 сентября 2016

Мой текущий ответ уже набрал наибольшее количество голосов, но я нашел лучший и более современный способ сделать это.

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)))));
2 голосов
/ 22 декабря 2009

Вам нужно постоянно сортировать свою коллекцию? При получении пар вам нужно, чтобы они всегда были отсортированы, или это только несколько раз (может быть, просто для представления)? Насколько большой вы ожидаете, что ваша коллекция будет? Есть много факторов, которые могут помочь вам решить, какой метод использовать.

Если вам нужно, чтобы коллекция сортировалась постоянно, даже если вы вставляете или удаляете элементы, и скорость вставки не является проблемой, возможно, вам следует реализовать какой-то SortedObservableCollection, например, упомянутый @Gerrie Schenck, или проверить эта реализация .

Если вам нужна ваша коллекция, отсортированная всего несколько раз, используйте:

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

Это займет некоторое время для сортировки коллекции, но даже в этом случае это может быть лучшим решением в зависимости от того, что вы делаете с ней.

1 голос
/ 30 января 2014

Хорошо, так как у меня были проблемы с получением 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 голос
/ 13 октября 2013

Ни один из этих ответов не работал в моем случае. Либо потому, что он испортил связывание, либо требует так много дополнительного кодирования, что это своего рода кошмар, или ответ просто сломан. Итак, вот еще один более простой ответ, который я подумал. Это намного меньше кода, и он остается той же наблюдаемой коллекцией с дополнительным методом 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 голос
/ 22 декабря 2009

Один из способов - преобразовать его в список, а затем вызвать Sort (), предоставив делегат сравнения. Что-то вроде: -

(непроверенные)

my_collection.ToList().Sort((left, right) => left == right ? 0 : (left > right ? -1 : 1));
1 голос
/ 26 марта 2013

Мне кажется, это самое элегантное решение:

http://www.xamlplayground.org/post/2009/07/18/Use-CollectionViewSource-effectively-in-MVVM-applications.aspx

1 голос
/ 05 октября 2015

Это сработало для меня, нашел это где-то давно.

// 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);
1 голос
/ 12 декабря 2015

Вот что я делаю с расширениями 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 голос
/ 26 марта 2013

Какого черта, я также добавлю ответ, который быстро выложен вместе ... он немного похож на некоторые другие реализации здесь, но я добавлю его в любом случае:

(едва проверено, надеюсь, я не смущаюсь)

Давайте сначала сформулируем некоторые цели (мои предположения):

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 голос
/ 22 декабря 2009

Создать новый класс SortedObservableCollection, извлечь его из ObservableCollection и реализовать IComparable<Pair<ushort, string>>.

...