SortedList, кажется, не сортирует

Я сделал класс-оболочку вокруг SortedList. Я добавляю объекты в этот класс, ожидая, что они будут автоматически отсортированы по алфавиту, но когда я привязываюсь к ListBox в WPF, я вижу их в несортированном порядке.

public class SortedObservableCollection<T> : ICollection<T>, INotifyPropertyChanged, INotifyCollectionChanged where T : INotifyPropertyChanged//, IComparable<T>
{
    private readonly SortedList<string,T> _innerSortedList;

    public SortedObservableCollection()
    {
        _innerSortedList = new SortedList<string, T>();
    }

    public void Add(T item)
    {
        _innerSortedList.Add(item.ToString(), item);
        this.OnPropertyChanged("Count");
        this.OnPropertyChanged("Item[]");
        this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item);
        item.PropertyChanged += ItemPropertyChanged;
    }

    void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        _innerSortedList.Clear();
    }

    public bool Contains(T item)
    {
        return _innerSortedList.ContainsKey(item.ToString());
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _innerSortedList.Count; }
    }

    public bool IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    public bool Remove(T item)
    {
        bool success = _innerSortedList.Remove(item.ToString());
        if (success)
        {
            item.PropertyChanged -= ItemPropertyChanged;
            this.OnPropertyChanged("Count");
            this.OnPropertyChanged("Item[]");
            this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item);
        }
        return success;
    }

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _innerSortedList.GetEnumerator();
    }

    protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action, object item)
    {
        if (this.CollectionChanged != null)
        {
            this.CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public event NotifyCollectionChangedEventHandler CollectionChanged;
}

Чтобы связать, я просто делаю:

SortedObservableCollection<IRCUser> Users { get; private set; }
.. fill users...
lstUsers.ItemsSource = users;

Пример ввода:

5Muhammad0
2Muhammad1
5Muhammad2

Вывод также показывает подобное, с теми, которые начинаются с 2, 4 и т. д., пронизанными между 5-ми.

Примечание. Мой класс IRCUser действительно реализовал IComparable, но, поскольку теперь мне нужна только алфавитная сортировка, я закомментировал внедрение, надеясь, что сортировка по умолчанию сработает.

ПРИМЕЧАНИЕ 2. Я переопределил метод toString(), о котором забыл упомянуть:

public override string ToString()
{
    return (int)Type + Nick;
}

ОБНОВИТЬ :

Кажется, внутри sortedList поддерживается правильный порядок, но он не передается ListBox в правильном порядке...


person sprocket12    schedule 15.07.2013    source источник
comment
(int)Type не является string, компилируется ли ваше переопределение?   -  person Jodrell    schedule 15.07.2013
comment
поскольку ваш тип ключа SortedList - string, вы получите реализацию IComparable<string> по умолчанию, которая является алфавитной.   -  person Jodrell    schedule 15.07.2013


Ответы (4)


Вы неправильно создаете объект события NotifyCollectionChangedEventArgs. Этот объект имеет разные перегрузки конструктора в зависимости от действия. Вы должны использовать перегрузку, которая использует индекс нового элемента при создании нового элемента:

new NotifyCollectionChangedEventArgs(action, item, index)

Вот цитата из MSDN:

Инициализирует новый экземпляр класса NotifyCollectionChangedEventArgs, который описывает изменение добавления или удаления.

NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction, Object, Int32)

ОБНОВЛЕНИЕ 0

Также лучше не использовать перегрузку метода ToString для сравнения предметов, а использовать для этого специальный IComparer<TKey>. В вашем случае правильный код выглядит так:

public void Add(T item)
{
    var key = item.ToString();
    _innerSortedList.Add(key, item);
    this.OnPropertyChanged("Count");
    this.OnPropertyChanged("Item[]");
    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _innerSortedList.IndexOfKey(key)));
    item.PropertyChanged += ItemPropertyChanged;
}

public bool Remove(T item)
{
    var indexOfKey = _innerSortedList.IndexOfKey(item.ToString());
    if (indexOfKey == -1)
        return false;
    _innerSortedList.RemoveAt(indexOfKey);
    item.PropertyChanged -= ItemPropertyChanged;
    this.OnPropertyChanged("Count");
    this.OnPropertyChanged("Item[]");
    this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item,
        indexOfKey));
    return true;
}

public IEnumerator<T> GetEnumerator()
{
    return _innerSortedList.Values.GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
    var handler = this.CollectionChanged;
    if (handler != null)
    {
        handler(this, args);
    }
}
person Vyacheslav Volkov    schedule 15.07.2013

Проблема с

_innerSortedList.Add(item.ToString(), item);

Предположим, что если элемент имеет тип Project.Test.CustomEntity, то item.ToString() даст вам «Project.Test.CustomEntity», который сортируется по полному имени объекта, а не по значению.

вам нужно написать собственный сортировщик на основе селектора свойств вашего T.

Взгляните на эту статью.

person vendettamit    schedule 15.07.2013

Я не уверен, но:

1) Если в пользовательском интерфейсе есть шаблон данных, а образец вывода, который вы там показали, не является результатом IRCUser.ToString(), то, возможно, ToString() не предоставляет «хорошее» хэш-значение для сортировки.

2) Возможно, вам будет лучше использовать PagedCollectionView, чтобы обернуть вашу коллекцию и установить там порядок.

person Arielr    schedule 15.07.2013

Вы сортируете свои данные по item.ToString(), что может быть не очень полезным значением для сортировки.

Если оно не переопределено, оно будет именем типа класса и, следовательно, одинаковым для всего, что вы добавляете.


Это, очевидно, приводит к вопросу, как следует сортировать общие данные. Для этого предназначен IComparable<T>.


Вы заметите, что существует несколько SortedList<T> конструкторов которые позволяют передать реализацию IComparable для определения собственного порядка.

В любом случае, если вы хотите использовать реализацию строки IComparable по умолчанию, вам нужно будет использовать строки, которые отличаются в соответствии с желаемым порядком. Не вводите имена, которые вообще не отличаются.

person Jodrell    schedule 15.07.2013
comment
Да, я переопределил его, чтобы дать сортируемое по алфавиту значение. - person sprocket12; 15.07.2013
comment
@MuhammadA, я не вижу этого в вопросе. Пожалуйста, покажите соответствующую реализацию IRCUser и экземпляр Users. - person Jodrell; 15.07.2013