1

我围绕 SortedList 创建了一个包装类。我将对象添加到此类,希望它们按字母顺序自动排序,但是当我绑定到 WPF 中的 ListBox 时,我会看到未排序的顺序。

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正确的顺序传递给...

4

4 回答 4

1

您没有正确创建事件对象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);
    }
}
于 2013-07-15T16:57:41.440 回答
0

您正在对数据进行排序,item.ToString()这可能不是一个非常有用的排序值。

除非它被覆盖,否则它将是类的类型名称,因此对于您添加的所有内容都是相同的。


这显然会引出一个问题,通用数据应该如何排序。这是IComparable<T>为了什么。


你会注意到有几个SortedList<T> 构造函数允许你传递一个IComparable实现来定义你自己的顺序。

在任何情况下,如果您想使用IComparable字符串的默认实现,您将需要根据您想要的顺序使用不同的字符串。不要键入根本没有区别的名称。

于 2013-07-15T16:52:53.350 回答
0

问题在于

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

假设如果 item 是类型,Project.Test.CustomEntity那么item.ToString()会给你“ Project.Test.CustomEntity”,它是按实体的全名而不是按值排序的。

您需要根据 T 的属性选择器编写自定义排序器。

看看这篇文章

于 2013-07-15T16:58:24.250 回答
0

我不确定,但是:

1)如果 UI 有一个数据模板,并且您显示的示例输出不是 IRCUser.ToString() 的结果 - 那么 ToString() 可能没有为排序提供“好的”哈希值。

2) 使用 PagedCollectionView 包装您的收藏并在那里设置排序可能会为您提供更好的服务。

于 2013-07-15T17:00:04.717 回答