Windows 10 ScrollIntoView()不会滚动到列表视图中间的项目

Ama*_*eno 16 c# xaml visual-studio windows-10 uwp

我有一个包含20个项目的Listview.我想以编程方式滚动Listview.

ListView?.ScrollIntoView(ListView.Items[0])
Run Code Online (Sandbox Code Playgroud)

将列表视图滚动到第一个项目.

ListView?.ScrollIntoView(ListView.Items.Count - 1)
Run Code Online (Sandbox Code Playgroud)

将列表视图滚动到页面底部.

但是,我无法使用相同的功能将列表视图滚动到中间的项目.

Eg: ListView?.ScrollIntoView(ListView.Items[5])
Run Code Online (Sandbox Code Playgroud)

应该滚动并带我到列表的第5项.但它把我带到了列表的第一项.

如果通过一些解决方法可以实现这种行为,那会很棒吗?

Jus*_* XL 26

我认为你正在寻找的是一种实际将元素滚动到顶部的方法ListView.

这篇文章中,我创建了一个扩展方法,滚动到一个特定的元素ScrollViewer.

在你的情况下,这个想法是一样的.

您需要先ScrollViewer在您的实例中找到实例ListView,然后找到要滚动到的实际项目,即a ListViewItem.

这是一个获得的扩展方法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;
}
Run Code Online (Sandbox Code Playgroud)

一旦我得到ScrollViewer实例,我就创建了两个扩展方法,分别根据索引或附加对象滚动到一个项目.从而ListViewGridView共享相同的基类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;
    }
}
Run Code Online (Sandbox Code Playgroud)


一种更简单的方法,但没有动画

您还可以ScrollIntoView通过指定第二个参数来使用新的重载,以确保项目在顶部边缘对齐; 但是,这样做在我之前的扩展方法中没有平滑的滚动过渡.

MyListView?.ScrollIntoView(MyListView.Items[5], ScrollIntoViewAlignment.Leading);
Run Code Online (Sandbox Code Playgroud)

  • 考虑将逻辑包装在附加属性中.看看http://stackoverflow.com/q/8370209/231837 (2认同)