UWP/C#:ObservableCollection 就地排序(w/o 滚动)
UWP/C#: ObservableCollection sort in-place (w/o scrolling)
在 UWP 应用程序中,我正在尝试对绑定到 ListView
的 ObservableCollection
进行排序 - 因此 collection.OrderBy(..)
(创建新集合)不是一个选项。
直到现在我都使用这个扩展方法:
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);
}
}
不幸的是,当前的 'scrolling-offset' 由于 source.Clear()
而被重置,相应的 ListView
一直滚动回到顶部 - 这是非常糟糕的用户体验。
有什么想法吗?
我刚才在处理同样的问题,结果是这样的:
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);
}
}
}
它可能不是最好的解决方案,它在 L640 等手机上有点滞后,但它可以工作。如果您需要滚动到 ListView
中的某个项目,您可以使用此方法:
YourListView.ScrollIntoView(ListViewItemToScrollTo);
您可以尝试创建一个 temp 集合,其中包含原始集合中的所有项目,对其进行排序,然后遍历其项目并仅重新排序这些项目其中的位置需要更新。像这样 -
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);
}
}
此外,您会希望 ListView
在项目动画时保持滚动偏移。这可以通过设置 -
来完成
<ItemsPanelTemplate>
<ItemsStackPanel ItemsUpdatingScrollMode="KeepScrollOffset" />
</ItemsPanelTemplate>
我发现这个与 UX 相关的问题非常有趣,我什至最终为它创建了一些 demo project。 :) 下面的 gif 演示了最终结果。对我来说,它提供了更好的体验,因为我在视觉上知道哪些项目会或不会通过排序重新定位。
在 UWP 应用程序中,我正在尝试对绑定到 ListView
的 ObservableCollection
进行排序 - 因此 collection.OrderBy(..)
(创建新集合)不是一个选项。
直到现在我都使用这个扩展方法:
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);
}
}
不幸的是,当前的 'scrolling-offset' 由于 source.Clear()
而被重置,相应的 ListView
一直滚动回到顶部 - 这是非常糟糕的用户体验。
有什么想法吗?
我刚才在处理同样的问题,结果是这样的:
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);
}
}
}
它可能不是最好的解决方案,它在 L640 等手机上有点滞后,但它可以工作。如果您需要滚动到 ListView
中的某个项目,您可以使用此方法:
YourListView.ScrollIntoView(ListViewItemToScrollTo);
您可以尝试创建一个 temp 集合,其中包含原始集合中的所有项目,对其进行排序,然后遍历其项目并仅重新排序这些项目其中的位置需要更新。像这样 -
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);
}
}
此外,您会希望 ListView
在项目动画时保持滚动偏移。这可以通过设置 -
<ItemsPanelTemplate>
<ItemsStackPanel ItemsUpdatingScrollMode="KeepScrollOffset" />
</ItemsPanelTemplate>
我发现这个与 UX 相关的问题非常有趣,我什至最终为它创建了一些 demo project。 :) 下面的 gif 演示了最终结果。对我来说,它提供了更好的体验,因为我在视觉上知道哪些项目会或不会通过排序重新定位。