窗10 ScrollIntoView()不滚动的物品在ListView中间 [英] Windows 10 ScrollIntoView() is not scrolling to the items in the middle of a listview

查看:679
本文介绍了窗10 ScrollIntoView()不滚动的物品在ListView中间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这20个项目列表视图。我希望以编程方式滚动列表视图。

 的ListView?.ScrollIntoView(ListView.Items [0])

将滚动列表视图的第一个项目。

 的ListView .ScrollIntoView(ListView.Items.Count  -  1)?

将滚动列表视图页面的底部。



不过,我不能使用相同的函数滚动在中间的列表视图到项目中。

 例如:ListView控件.ScrollIntoView(ListView.Items [5])

应滚动并带我到列表中的第五项。而是它带我到列表中的第一个项目。



将是巨大的,如果这种行为可以通过一些变通方法来实现吗?


< DIV CLASS =h2_lin>解决方案

我知道你在寻找的是一个方法以实际滚动的一个元素的的ListView顶部



这个帖子,我创建了滚动到特定元素中的的ScrollViewer

$扩展方法b
$ b

我们的想法是你的情况是一样的。



您需要先找到的ScrollViewer 比如你的的ListView 之内,那么实际的项目滚动到,那就是,一个 ListViewItem的



下面是一个扩展方法来获得的ScrollViewer

 公共静态的ScrollViewer GetScrollViewer(此DependencyObject元素)
{
如果(元素的ScrollViewer)
{
回报率(的ScrollViewer)元素;
}

的for(int i = 0; I< VisualTreeHelper.GetChildrenCount(元);我++)
{
VAR孩子= VisualTreeHelper.GetChild(元素,一世);

VAR的结果= GetScrollViewer(孩子);
如果(结果== NULL)
{
继续;
}
,否则
{
返回结果;
}
}

返回NULL;
}



一旦我得到了的ScrollViewer 举例来说,我已经创建了两个以上的扩展方法滚动到分别基于其索引或附着对象的项目。由于的ListView GridView控件共享相同的基类 ListViewBase 。这两种扩展方法也应适用 GridView控件



更新



基本上,这些方法会先找到该项目,如果它已经呈现,然后滚动到它的时候了。如果该项目是,这意味着在虚拟化上,而该项目尚未实现。因此,首先实现的项目,叫 ScrollIntoViewAsync (基于任务的方法来包装内置 ScrollIntoView ,同 ChangeViewAsync ,它提供了更清洁的代码),计算出的位置和保存。因为现在我知道滚动到的位置,我需要先滚动项目的所有的方式回到它原来的位置的立即的(即没有动画),最后滚动到动画所需的位置。

 公共异步静态任务ScrollToIndex(这ListViewBase listViewBase,INT指数)
{
布尔isVirtualizing =默认(布尔);
双previousHorizo​​ntalOffset =默认值(双),previousVerticalOffset =默认值(双);

//获取的ScrollViewer withtin在ListView / GridView的
变种的ScrollViewer = listViewBase.GetScrollViewer();
//获取SelectorItem滚动到
VAR selectorItem = listViewBase.ContainerFromIndex(指数)为SelectorItem;

//当它是空,意味着虚拟化是和该项目一直未能实现尚未
如果(selectorItem == NULL)
{
isVirtualizing = TRUE ;

previousHorizo​​ntalOffset = scrollViewer.Horizo​​ntalOffset;
previousVerticalOffset = scrollViewer.VerticalOffset;

//调用基于任务的ScrollIntoViewAsync实现项目
等待listViewBase.ScrollIntoViewAsync(listViewBase.Items [指数]);

//这一次的项目不能为空再次
selectorItem =(SelectorItem)listViewBase.ContainerFromIndex(指数);
}

//计算位置的对象,以便知道多少滚动至
无功变换= selectorItem.TransformToVisual((的UIElement)scrollViewer.Content);
变种位置= transform.TransformPoint(新点(0,0));

//虚拟化时,滚动回到原来的位置没有动画
如果(isVirtualizing)
{
等待scrollViewer.ChangeViewAsync(previousHorizo​​ntalOffset,previousVerticalOffset,真正的);
}

//滚动到所需位置的动画!
scrollViewer.ChangeView(position.X,position.Y,NULL);
}

公共异步静态任务ScrollToItem(这ListViewBase listViewBase,目标项)
{
布尔isVirtualizing =默认值(布尔);
双previousHorizo​​ntalOffset =默认值(双),previousVerticalOffset =默认值(双);

//获取的ScrollViewer withtin在ListView / GridView的
变种的ScrollViewer = listViewBase.GetScrollViewer();
//获取SelectorItem滚动到
VAR selectorItem = listViewBase.ContainerFromItem(项目)为SelectorItem;

//当它是空,意味着虚拟化是和该项目一直未能实现尚未
如果(selectorItem == NULL)
{
isVirtualizing = TRUE ;

previousHorizo​​ntalOffset = scrollViewer.Horizo​​ntalOffset;
previousVerticalOffset = scrollViewer.VerticalOffset;

//调用基于任务的ScrollIntoViewAsync实现项目
等待listViewBase.ScrollIntoViewAsync(项目);

//这一次的项目不能为空再次
selectorItem =(SelectorItem)listViewBase.ContainerFromItem(项目);
}

//计算位置的对象,以便知道多少滚动至
无功变换= selectorItem.TransformToVisual((的UIElement)scrollViewer.Content);
变种位置= transform.TransformPoint(新点(0,0));

//虚拟化时,滚动回到原来的位置没有动画
如果(isVirtualizing)
{
等待scrollViewer.ChangeViewAsync(previousHorizo​​ntalOffset,previousVerticalOffset,真正的);
}

//滚动到所需位置的动画!
scrollViewer.ChangeView(position.X,position.Y,NULL);
}

公共静态异步任务ScrollIntoViewAsync(这ListViewBase listViewBase,目标项)
{
变种TCS =新TaskCompletionSource<对象>();
变种的ScrollViewer = listViewBase.GetScrollViewer();

&事件处理LT; ScrollViewerViewChangedEventArgs> viewChanged =(S,E)=> tcs.TrySetResult(NULL);

{
scrollViewer.ViewChanged + = viewChanged;
listViewBase.ScrollIntoView(项目,ScrollIntoViewAlignment.Leading);
等待tcs.Task;
}
终于
{
scrollViewer.ViewChanged - = viewChanged;
}
}

公共静态异步任务ChangeViewAsync(此的ScrollViewer ScrollViewer中,翻倍?horizo​​ntalOffset,翻倍?verticalOffset,布尔disableAnimation)
{
变种TCS =新TaskCompletionSource<对象>();

&事件处理LT; ScrollViewerViewChangedEventArgs> viewChanged =(S,E)=> tcs.TrySetResult(NULL);

{
scrollViewer.ViewChanged + = viewChanged;
scrollViewer.ChangeView(horizo​​ntalOffset,verticalOffset,空,disableAnimation);
等待tcs.Task;
}
终于
{
scrollViewer.ViewChanged - = viewChanged;
}
}






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



您也可以使用 ScrollIntoView 通过指定所述第二参数,以确保该项目上的顶部边缘对齐;然而,这样做不会有我以前的扩展方法平滑滚动过渡。

  MyListView?.ScrollIntoView(MyListView.Items [5],ScrollIntoViewAlignment.Leading); 


I have a Listview with 20 items in it. I want to scroll the Listview programmatically.

ListView?.ScrollIntoView(ListView.Items[0])

will scroll the listview to the first item.

ListView?.ScrollIntoView(ListView.Items.Count - 1)

will scroll the listview to the bottom of the page.

However, I am unable to use the same function to scroll the listview to an item in middle.

Eg: ListView?.ScrollIntoView(ListView.Items[5])

should scroll and take me to the 5th item of the list. But instead its taking me to the first item of the list.

Would be great if this behaviour can be achieved with some workaround?

解决方案

I think what you are looking for is a method to actually scroll an element to the top of the ListView.

In this post, I created an extension method that scrolls to a particular element within a ScrollViewer.

The idea is the same in your case.

You need to first find the ScrollViewer instance within your ListView, then the actual item to scroll to, that is, a ListViewItem.

Here is an extension method to get the 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;
}

Once I get the ScrollViewer instance, I have created two more extension methods to scroll to an item based on its index or attached object respectively. Since ListView and GridView are sharing the same base class ListViewBase. These two extension methods should also work for GridView.

Update

Basically, the methods will first find the item, if it's already rendered, then scroll to it right away. If the item is null, it means the virtualization is on and the item has yet to be realized. So to realize the item first, call ScrollIntoViewAsync (task-based method to wrap the built-in ScrollIntoView, same as ChangeViewAsync, which offers much cleaner code), calculate the position and save it. Since now I know the position to scroll to, I need to first scroll the item all the way back to its previous position instantly (i.e. without animation), and then finally scroll to the desired position with animation.

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;
    }
}


A simpler approach, but without animation

You can also use the new overload of ScrollIntoView by specifying the second parameter to make sure the item is aligned on the top edge; however, doing so doesn't have the smooth scrolling transition in my previous extension methods.

MyListView?.ScrollIntoView(MyListView.Items[5], ScrollIntoViewAlignment.Leading);

这篇关于窗10 ScrollIntoView()不滚动的物品在ListView中间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆