This extension method eliminates the need to sort the entire list.
Instead, it inserts each new item in place. So the list is always remains sorted.
It turns out that this method just works when a lot of the other methods fail due to missing notifications when the collection changes. And it is rather fast.
The code below should be bulletproof; it has been extensively tested in a large-scale production environment.
To use:
// Call on dispatcher.
ObservableCollection<MyClass> collectionView = new ObservableCollection<MyClass>();
var p1 = new MyClass() { Key = "A" }
var p2 = new MyClass() { Key = "Z" }
var p3 = new MyClass() { Key = "D" }
collectionView.InsertInPlace(p1, o => o.Key);
collectionView.InsertInPlace(p2, o => o.Key);
collectionView.InsertInPlace(p3, o => o.Key);
// The list will always remain ordered on the screen, e.g. "A, D, Z" .
// Insertion speed is Log(N) as it uses a binary search.
And the extension method:
/// <summary>
/// Inserts an item into a list in the correct place, based on the provided key and key comparer. Use like OrderBy(o => o.PropertyWithKey).
/// </summary>
public static void InsertInPlace<TItem, TKey>(this ObservableCollection<TItem> collection, TItem itemToAdd, Func<TItem, TKey> keyGetter)
{
int index = collection.ToList().BinarySearch(keyGetter(itemToAdd), Comparer<TKey>.Default, keyGetter);
collection.Insert(index, itemToAdd);
}
And the binary search extension method:
/// <summary>
/// Binary search.
/// </summary>
/// <returns>Index of item in collection.</returns>
/// <notes>This version tops out at approximately 25% faster than the equivalent recursive version. This 25% speedup is for list
/// lengths more of than 1000 items, with less performance advantage for smaller lists.</notes>
public static int BinarySearch<TItem, TKey>(this IList<TItem> collection, TKey keyToFind, IComparer<TKey> comparer, Func<TItem, TKey> keyGetter)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
int lower = 0;
int upper = collection.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer.Compare(keyToFind, keyGetter.Invoke(collection[middle]));
if (comparisonResult == 0)
{
return middle;
}
else if (comparisonResult < 0)
{
upper = middle - 1;
}
else
{
lower = middle + 1;
}
}
// If we cannot find the item, return the item below it, so the new item will be inserted next.
return lower;
}