获取 GridView/ListView 中的第一个可见项 [英] Get first visible item in a GridView/ListView

查看:28
本文介绍了获取 GridView/ListView 中的第一个可见项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在一个页面中显示一组图片.我使用 GridView 来显示图片.但是,当用户调整屏幕大小使其变窄时,我切换到 ListView.
现在的问题是同步两个列表的滚动位置.我的解决方法是,
1.获取第一个列表的第一个可见项.
2. 使用 ScrollIntoView
将第二个列表滚动到该项目但是,我无法在 GridView/ListView 中看到任何提供第一个信息的属性.有什么想法吗?
也感谢任何其他方式做到这一点.

I'm showing a set of pictures in a page. I use a GridView to show the pictures. However, when the user resizes the screen to make it narrow, I switch to a ListView.
The problem now is synchronizing the scroll position for the two lists. My approach to the solution is,
1. Get the first visible item of the first list.
2. Scroll the second list to that item using ScrollIntoView
However I'm unable to see any property in GridView/ListView that gives me the first information. Any ideas?
Also any other ways of doing this are appreciated.

推荐答案

这似乎就是我第一次尝试的方式.您可以使用GridView/ListViewItemsPanelRoot 属性并获取面板的Children,然后使用TransformToVisual().TransformPoint() 相对于每个孩子的列表控件,找到第一个可见的.

That seems to be just about the way I would first try to do it. You can use the ItemsPanelRoot property of the GridView/ListView and get the Children of the panel, then use TransformToVisual().TransformPoint() relative to the list control on each child to find the first one that is visible.

我能想到的一个问题是 ScrollIntoView() 将滚动一个列表中第一个在视图端口中的项目以在另一个列表中显示为最后一个.也许您可以从列表控件的模板中获取 ScrollViewer(例如,通过使用 VisualTreeHelper)并首先滚动到列表的开头?

The one gotcha I can think of is when ScrollIntoView() would scroll the item that was first in view port in one list to show as last in view in the other one. Maybe you could get the ScrollViewer from the template of the list control (e.g. by using VisualTreeHelper) and scroll to the beginning of the list first?

最简单的方法可能是滚动到列表中与列表中出现的列表相同的相对偏移量.它可能不是很精确,但它可以工作.

The most simple way to do it all might be to just scroll to the same relative offset in the list coming into view as the one going out. It might not be very precise, but it could work.

您甚至可以将一个列表中的元素动画过渡到另一个列表中的元素.

You could even do a nice animated transition of elements in one list into the elements in the other one.

*更新

我四处询问,似乎我忘记了 GridViewListView 中的默认面板 - ItemsWrapGridItemsStackPanel 包含一个 FirstVisibleIndex 属性,可用于获取对象然后调用 ScrollIntoView() 在列表控件上,这反过来需要一个 ScrollIntoViewAlignment 枚举你可以用来说你想要滚动到-item 是第一个可见的(对齐到前缘).

I asked around and it seems like I forgot that the default panels in GridView and ListView - the ItemsWrapGrid and ItemsStackPanel contain a FirstVisibleIndex property that could be used to get the object and then call ScrollIntoView() on the list control, which in turns takes a ScrollIntoViewAlignment enum you can use to say you want the scrolled-to-item to be the first visible (aligned to the leading edge).

*更新 2

对于 ListViewBase - 您还可以使用 ListViewPersistenceHelper 获取和设置相对偏移量.

For ListViewBase - you can also use the ListViewPersistenceHelper to get and set relative offsets.

即将到来的 WinRT XAML 工具包更新可能会有所帮助,因为它允许您简单地调用:gridView.SynchronizeScrollOffset(listView);,反之亦然.

This upcoming update to WinRT XAML Toolkit might be helpful as it would allow you to simply call: gridView.SynchronizeScrollOffset(listView); or vice versa.

public static class ItemsControlExtensions
{
    public static ScrollViewer GetScrollViewer(this ItemsControl itemsControl)
    {
        return itemsControl.GetFirstDescendantOfType<ScrollViewer>();
    }

    public static int GetFirstVisibleIndex(this ItemsControl itemsControl)
    {
        // First checking if no items source or an empty one is used
        if (itemsControl.ItemsSource == null)
        {
            return -1;
        }

        var enumItemsSource = itemsControl.ItemsSource as IEnumerable;

        if (enumItemsSource != null && !enumItemsSource.GetEnumerator().MoveNext())
        {
            return -1;
        }

        // Check if a modern panel is used as an items panel
        var sourcePanel = itemsControl.ItemsPanelRoot;

        if (sourcePanel == null)
        {
            throw new InvalidOperationException("Can't get first visible index from an ItemsControl with no ItemsPanel.");
        }

        var isp = sourcePanel as ItemsStackPanel;

        if (isp != null)
        {
            return isp.FirstVisibleIndex;
        }

        var iwg = sourcePanel as ItemsWrapGrid;

        if (iwg != null)
        {
            return iwg.FirstVisibleIndex;
        }

        // Check containers for first one in view
        if (sourcePanel.Children.Count == 0)
        {
            return -1;
        }

        if (itemsControl.ActualWidth == 0)
        {
            throw new InvalidOperationException("Can't get first visible index from an ItemsControl that is not loaded or has zero size.");
        }

        for (int i = 0; i < sourcePanel.Children.Count; i++)
        {
            var container = (FrameworkElement)sourcePanel.Children[i];
            var bounds = container.TransformToVisual(itemsControl).TransformBounds(new Rect(0, 0, container.ActualWidth, container.ActualHeight));

            if (bounds.Left < itemsControl.ActualWidth &&
                bounds.Top < itemsControl.ActualHeight &&
                bounds.Right > 0 &&
                bounds.Bottom > 0)
            {
                return itemsControl.IndexFromContainer(container);
            }
        }

        throw new InvalidOperationException();
    }

    public static void SynchronizeScrollOffset(this ItemsControl targetItemsControl, ItemsControl sourceItemsControl, bool throwOnFail = false)
    {
        var firstVisibleIndex = sourceItemsControl.GetFirstVisibleIndex();

        if (firstVisibleIndex == -1)
        {
            if (throwOnFail)
            {
                throw new InvalidOperationException();
            }

            return;
        }

        var targetListBox = targetItemsControl as ListBox;

        if (targetListBox != null)
        {
            targetListBox.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)));
            return;
        }

        var targetListViewBase = targetItemsControl as ListViewBase;

        if (targetListViewBase != null)
        {
            targetListViewBase.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)), ScrollIntoViewAlignment.Leading);
            return;
        }

        var scrollViewer = targetItemsControl.GetScrollViewer();

        if (scrollViewer != null)
        {
            var container = (FrameworkElement) targetItemsControl.ContainerFromIndex(firstVisibleIndex);
            var position = container.TransformToVisual(scrollViewer).TransformPoint(new Point());
            scrollViewer.ChangeView(scrollViewer.HorizontalOffset + position.X, scrollViewer.VerticalOffset + position.Y, null);
        }
    }
}

这篇关于获取 GridView/ListView 中的第一个可见项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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