我认为您正在寻找的实际上 是一种ListView
将元素滚动到的顶部的方法。
ScrollViewer
我创建了一个扩展方法是滚动到内的特定元素 。首先
ScrollViewer
,我需要找到ListView
此类项目的实际实例,或滚动到nextListViewItem
。ScrollViewer
这是获得的扩展方法:。public static ScrollViewer GetScrollViewer(this DependencyObject element) { if (element is ScrollViewer) { return (ScrollViewer)element; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) { var child = VisualTreeHelper.GetChild(element, i); var result = GetScrollViewer(child); if (result == null) { continue; } else { return result; } } return null; }
ScrollViewer
我创建了两个扩展方法,以基于例如索引或链接对象滚动到某个项目。ListView
并 共享GridView
相同的基类ListViewBase
。这两种扩展方法GridView
也适用。。更新
基本上,该方法首先找到渲染的项目,然后立即滚动。如果该项为
null
,则表示已启用虚拟化并且尚未实现该项。ScrollIntoViewAsync
打电话给我们首先实现该项目 。( 与ScrollIntoView
包装内置 的基于任务的方法ChangeViewAsync
相同(提供了更简洁的代码))计算并存储位置。现在您知道要滚动的位置了,您必须首先立即将项目滚动回到上一个位置(即无动画),然后使用动画滚动到所需的位置。
没有动画的更简单方法public async static Task ScrollToIndex(this ListViewBase listViewBase, int index) { bool isVirtualizing = default(bool); double previousHorizontalOffset = default(double), previousVerticalOffset = default(double); // get the ScrollViewer withtin the ListView/GridView var scrollViewer = listViewBase.GetScrollViewer(); // get the SelectorItem to scroll to var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem; // when it's null, means virtualization is on and the item hasn't been realized yet if (selectorItem == null) { isVirtualizing = true; previousHorizontalOffset = scrollViewer.HorizontalOffset; previousVerticalOffset = scrollViewer.VerticalOffset; // call task-based ScrollIntoViewAsync to realize the item await listViewBase.ScrollIntoViewAsync(listViewBase.Items[index]); // this time the item shouldn't be null again selectorItem = (SelectorItem)listViewBase.ContainerFromIndex(index); } // calculate the position object in order to know how much to scroll to var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content); var position = transform.TransformPoint(new Point(0, 0)); // when virtualized, scroll back to previous position without animation if (isVirtualizing) { await scrollViewer.ChangeViewAsync(previousHorizontalOffset, previousVerticalOffset, true); } // scroll to desired position with animation! scrollViewer.ChangeView(position.X, position.Y, null); } public async static Task ScrollToItem(this ListViewBase listViewBase, object item) { bool isVirtualizing = default(bool); double previousHorizontalOffset = default(double), previousVerticalOffset = default(double); // get the ScrollViewer withtin the ListView/GridView var scrollViewer = listViewBase.GetScrollViewer(); // get the SelectorItem to scroll to var selectorItem = listViewBase.ContainerFromItem(item) as SelectorItem; // when it's null, means virtualization is on and the item hasn't been realized yet if (selectorItem == null) { isVirtualizing = true; previousHorizontalOffset = scrollViewer.HorizontalOffset; previousVerticalOffset = scrollViewer.VerticalOffset; // call task-based ScrollIntoViewAsync to realize the item await listViewBase.ScrollIntoViewAsync(item); // this time the item shouldn't be null again selectorItem = (SelectorItem)listViewBase.ContainerFromItem(item); } // calculate the position object in order to know how much to scroll to var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content); var position = transform.TransformPoint(new Point(0, 0)); // when virtualized, scroll back to previous position without animation if (isVirtualizing) { await scrollViewer.ChangeViewAsync(previousHorizontalOffset, previousVerticalOffset, true); } // scroll to desired position with animation! scrollViewer.ChangeView(position.X, position.Y, null); } public static async Task ScrollIntoViewAsync(this ListViewBase listViewBase, object item) { var tcs = new TaskCompletionSource<object>(); var scrollViewer = listViewBase.GetScrollViewer(); EventHandler<ScrollViewerViewChangedEventArgs> viewChanged = (s, e) => tcs.TrySetResult(null); try { scrollViewer.ViewChanged += viewChanged; listViewBase.ScrollIntoView(item, ScrollIntoViewAlignment.Leading); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; } } public static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, bool disableAnimation) { var tcs = new TaskCompletionSource<object>(); EventHandler<ScrollViewerViewChangedEventArgs> viewChanged = (s, e) => tcs.TrySetResult(null); try { scrollViewer.ViewChanged += viewChanged; scrollViewer.ChangeView(horizontalOffset, verticalOffset, null, disableAnimation); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; } }
ScrollIntoView
您还可以对第二个参数使用新的重载,以使该项目与顶部边缘对齐。但是,这不会导致以前的扩展方法中的平滑滚动过渡。MyListView?.ScrollIntoView(MyListView.Items[5], ScrollIntoViewAlignment.Leading);
-
二
ScrollIntoView使该项进入视图,并且不会滚动到句点。
当您呼叫成员并位于显示列表的底部时,该项向下滚动,直到您成为显示列表的最后一个成员。
当您呼叫成员并位于列表顶部时,该项目将向上滚动,直到您成为列表的第一个成员。
如果我呼叫了一个成员并且当前显示该成员,则根本不起作用。
-
三
Var sv = new ScrollViewerHelper().GetScrollViewer(listView); sv.UpdateLayout(); sv.ChangeView(0, sv.ExtentHeight, null);
GetScrollViewer方法:
public ScrollViewer GetScrollViewer(DependencyObject element) { if (element is ScrollViewer) { return (ScrollViewer)element; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) { var child = VisualTreeHelper.GetChild(element, i); var result = GetScrollViewer(child); if (result == null) { continue; } else { return result; } } return null; }