UWP/C#: ObservableCollection sort in-place (w/o scrolling)

感情迁移 提交于 2019-12-14 03:54:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!