问题
In an UWP app I'm trying to sort an ObservableCollection
that is bound to a ListView
- therefore collection.OrderBy(..)
(which creates a new collection) is not an option.
Until now I used this extension-method:
public static void Sort<TSource, TKey>(this
ObservableCollection<TSource> source, Func<TSource, TKey> keySelector)
{
List<TSource> sortedList = source.OrderBy(keySelector).ToList();
source.Clear();
foreach (var sortedItem in sortedList)
{
source.Add(sortedItem);
}
}
Unfortunately this way the current 'scrolling-offset' is resetted due to source.Clear()
and the corresponding ListView
scrolls all the way back to the top - which is pretty bad user-experience.
Any ideas?
回答1:
What you can try is to create a temp collection that contains all the items from your original collection, sort it, then loop through its items and only re-order the ones of which position needs to be updated. Something like this -
public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector)
{
var sortedSource = source.OrderBy(keySelector).ToList();
for (var i = 0; i < sortedSource.Count; i++)
{
var itemToSort = sortedSource[i];
// If the item is already at the right position, leave it and continue.
if (source.IndexOf(itemToSort) == i)
{
continue;
}
source.Remove(itemToSort);
source.Insert(i, itemToSort);
}
}
Also, you will want the ListView
to keep the scroll offset when items are animating. This can be done by setting -
<ItemsPanelTemplate>
<ItemsStackPanel ItemsUpdatingScrollMode="KeepScrollOffset" />
</ItemsPanelTemplate>
I found this UX related question really interesting and I even ended up creating a little demo project for it. :) The gif below demostrates the end result. To me it provides a better experience as I know visually, what items are or aren't repositioned by the sorting.
回答2:
I was dealing with the same problem a while ago and I ended up with this:
Func<TempoMarking, IComparable> selectorGetter = null;
// Setting the selectorGetter here
for (int i = 0; i < Collection.Count; i++)
{
for (int j = 0; j < Collection.Count - 1; j++)
{
YourType currentItem = Collection[j];
if (selectorGetter(currentItem).CompareTo(selectorGetter(Collection[j + 1])) == 1)
{
Collection.Remove(currentItem);
Collection.Insert(j + 1, currentItem);
}
}
}
It's probably not the best solution and it lags a bit on phones such as L640 but it works. If you need to scroll to some item in your ListView
you may use this method:
YourListView.ScrollIntoView(ListViewItemToScrollTo);
来源:https://stackoverflow.com/questions/44303000/uwp-c-observablecollection-sort-in-place-w-o-scrolling