Reputation: 5488
I made a wrapper class around SortedList. I add objects to this class expecting them to be automatically sorted alphabetically, but when I bind to a ListBox in WPF, I see then in unsorted order.
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;
}
To bind I simply do :
SortedObservableCollection<IRCUser> Users { get; private set; }
.. fill users...
lstUsers.ItemsSource = users;
Sample input :
5Muhammad0
2Muhammad1
5Muhammad2
The output also shows similar, with the ones beginning with 2, 4 etc riddled between the 5's.
Note: My IRCUser
class did implement IComparable, but since I only want an alphabetical sort now I commented the implentation out hoping the default sorting would kick in.
NOTE 2: I have override the toString() method, which I forgot to mention :
public override string ToString()
{
return (int)Type + Nick;
}
UPDATE :
It seems internally the sortedList
maintains the right order, but it is not passed to the ListBox
in the right order...
Upvotes: 1
Views: 532
Reputation: 4742
You are not correctly create the event object NotifyCollectionChangedEventArgs. This object has different overloads of constructor depending on the action. You must use the overload that uses the index of the new item when you create a new item:
new NotifyCollectionChangedEventArgs(action, item, index)
Here's quote from MSDN:
Initializes a new instance of the NotifyCollectionChangedEventArgs class that describes an Add or Remove change.
NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction, Object, Int32)
UPDATE 0
Also it is better not to use an overload of method ToString
to compare items, and use the special IComparer<TKey>
for this.
In your case, the correct code looks like this:
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);
}
}
Upvotes: 1
Reputation: 35726
You are sorting your data on item.ToString()
which may not be a very useful value to sort on.
Unless it is overridden, it will be the type name of the class and therefore the same for everything you add.
This obviously leads to the question, how should generic data be sorted. This is what IComparable<T>
is for.
You'll note that there are several SortedList<T>
constructors that allow you to pass an IComparable
implementation to define your own order.
In any case, if you want to use the default IComparable
implementation of string, you will need to use strings that differ in accordance with your desired order. Not type names that do not differ at all.
Upvotes: 0
Reputation: 447
I'm not sure but:
1) If the UI has a data template, and the sample output you showed there is not the result of IRCUser.ToString() - than, maybe, the ToString() does not provide a "good" hash value for sorting.
2) You may be better served by using a PagedCollectionView to wrap your collection, and set the ordering there.
Upvotes: 0
Reputation: 14687
The problem is with
_innerSortedList.Add(item.ToString(), item);
Let suppose if item is of type Project.Test.CustomEntity
then item.ToString()
will give you "Project.Test.CustomEntity
" which is getting sorted by Entity's fullname and not by the value.
you need to write a custom sorter based on property selector of your T.
Have a look at this article.
Upvotes: 0