Thanks Rachel, your solution gave me the clue, but still your solution is an answer because I can manually call the Sort method whenever I change the Order property, but I wanted to make it automatic. So I end up with a dynamic version of your code.
Based on the Rachel I approached to this solution.
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
public ObservableCollectionEx() : base() { }
public ObservableCollectionEx(List<T> l) : base(l) { }
public ObservableCollectionEx(IEnumerable<T> l) : base(l) { }
Func<IEnumerable<T>,IEnumerable<T>> sortFunction;
Action reset;
#region IndexOf
/// <summary>
/// Returns the index of the first object which meets the specified function
/// </summary>
/// <param name="keySelector">A bool function to compare each Item by</param>
/// <returns>The index of the first Item which matches the function</returns>
public int IndexOf(Func<T , bool> compareFunction)
{
return Items.IndexOf(Items.FirstOrDefault(compareFunction));
}
#endregion IndexOf
#region Sorting
/// <summary>
/// Sorts the items of the collection in ascending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
public void SetSort<TKey>(Func<T , TKey> keySelector)
{
sortFunction = list => list.OrderBy(keySelector);
InternalSort();
reset = () => SetSort(keySelector);
}
/// <summary>
/// Sorts the items of the collection in descending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
public void SetSortDescending<TKey>(Func<T , TKey> keySelector)
{
sortFunction = list => list.OrderByDescending(keySelector);
InternalSort();
reset = () => SetSortDescending(keySelector);
}
/// <summary>
/// Sorts the items of the collection in ascending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
/// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
public void SetSort<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
{
sortFunction = list => list.OrderBy(keySelector , comparer);
InternalSort();
reset = () => SetSort(keySelector , comparer);
}
/// <summary>
/// Sorts the items of the collection in descending order according to a key.
/// </summary>
/// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam>
/// <param name="keySelector">A function to extract a key from an item.</param>
/// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param>
public void SetSortDescending<TKey>(Func<T , TKey> keySelector , IComparer<TKey> comparer)
{
sortFunction = list => list.OrderByDescending(keySelector , comparer);
InternalSort();
reset = () => SetSortDescending(keySelector , comparer);
}
/// <summary>
/// Moves the items of the collection so that their orders are the same as those of the items provided.
/// </summary>
private void InternalSort()
{
UpdateTracking(null , Items.ToList());
}
private void MoveItemToItsLocation(T item)
{
var sortListCache = sortFunction(Items).ToList();
Move(IndexOf(item) , sortListCache.IndexOf(item));
}
#endregion Sorting
public void UpdateTracking(IEnumerable<T> oldItems , IEnumerable<T> newItems)
{
if (sortFunction == null) return;
PropertyChangedEventHandler changeTracker = (o , change) => { MoveItemToItsLocation((T)o); };
Action<T> attachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged += changeTracker);
Action<T> detachChangeTracker = o => o.ExecuteOnCast<INotifyPropertyChanged>(x => x.PropertyChanged -= changeTracker);
var greeting = new[] { attachChangeTracker , MoveItemToItsLocation };
var farwell = new[] { detachChangeTracker };
oldItems.ForEach(detachChangeTracker);
newItems.ForEach(attachChangeTracker , MoveItemToItsLocation);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
UpdateTracking(e.OldItems.SafeGet(x => x.Cast<T>()) , e.NewItems.SafeGet(x => x.Cast<T>()));
break;
case NotifyCollectionChangedAction.Reset:
UpdateTracking(Items.ToList() , null);
if (reset != null)
reset();
break;
default:
break;
}
}
}
I only made a little change to make it follow the changes in the underlying collection, so it will sort itself automatically after any changes made to underlying collection or any element in the underlying collection.