Windows runtime 获取GridView/ListView中的第一个可见项
我正在一页中显示一组图片。我使用GridView来显示图片。但是,当用户调整屏幕大小使其变窄时,我会切换到ListView。 现在的问题是同步两个列表的滚动位置。我的解决方法是, 1.获取第一个列表的第一个可见项。 2.使用ScrollIntoView将第二个列表滚动到该项目 但是,我无法在GridView/ListView中看到任何提供第一个信息的属性。有什么想法吗?Windows runtime 获取GridView/ListView中的第一个可见项,windows-runtime,winrt-xaml,windows-8.1,Windows Runtime,Winrt Xaml,Windows 8.1,我正在一页中显示一组图片。我使用GridView来显示图片。但是,当用户调整屏幕大小使其变窄时,我会切换到ListView。 现在的问题是同步两个列表的滚动位置。我的解决方法是, 1.获取第一个列表的第一个可见项。 2.使用ScrollIntoView将第二个列表滚动到该项目 但是,我无法在GridView/ListView中看到任何提供第一个信息的属性。有什么想法吗? 另外,我也非常感谢其他的方法。这似乎正是我第一次尝试的方法。您可以使用GridView/ListView的ItemsPanel
另外,我也非常感谢其他的方法。这似乎正是我第一次尝试的方法。您可以使用GridView/ListView的ItemsPanelRoot属性并获取面板的子级,然后相对于每个子级上的列表控件使用TransformToVisual.TransformPoint来查找第一个可见的子级 我能想到的一个问题是,当ScrollIntoView将一个列表中第一个在查看端口中的项目滚动到另一个列表中显示为最后一个在查看端口中的项目时。也许您可以从列表控件的模板获取ScrollViewer,例如使用VisualTreeHelper并首先滚动到列表的开头 完成这一切最简单的方法可能是滚动到进入视图的列表中与退出列表相同的相对偏移量。它可能不是很精确,但它可以工作 您甚至可以将一个列表中的元素转换为另一个列表中的元素 *更新 我四处询问,似乎忘记了GridView和ListView中的默认面板——ItemsWrapGrid和ItemsStackPanel包含一个属性,可以用来获取对象,然后调用list控件,它依次使用一个枚举,您可以使用该枚举表示希望滚动到的项是第一个与前缘对齐的可见项 *更新2 对于ListViewBase-还可以使用获取和设置相对偏移 WinRT XAML工具包即将进行的更新可能会有所帮助,因为它允许您只需调用:gridView.SynchronizeScrollOffsetlistView;反之亦然
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的ItemsPanelRoot属性并获取面板的子级,然后相对于每个子级上的列表控件使用TransformToVisual.TransformPoint来查找第一个可见的子级 我能想到的一个问题是,当ScrollIntoView将一个列表中第一个在查看端口中的项目滚动到另一个列表中显示为最后一个在查看端口中的项目时。也许您可以从列表控件的模板获取ScrollViewer,例如使用VisualTreeHelper并首先滚动到列表的开头 完成这一切最简单的方法可能是滚动到进入视图的列表中与退出列表相同的相对偏移量。它可能不是很精确,但它可以工作 您甚至可以将一个列表中的元素转换为另一个列表中的元素 *更新 我四处询问,似乎忘记了GridView和ListView中的默认面板——ItemsWrapGrid和ItemsStackPanel包含一个属性,可以用来获取对象,然后调用list控件,它依次使用一个枚举,您可以使用该枚举表示希望滚动到的项是第一个与前缘对齐的可见项 *更新2 对于ListViewBase-还可以使用获取和设置相对偏移 WinRT XAML工具包即将进行的更新可能会有所帮助,因为它允许您只需调用:gridView.SynchronizeScrollOffsetlistView;反之亦然
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);
}
}
}
FirstVisibleIndex正是我想要的。非常感谢FirstVisibleIndex正是我想要的。非常感谢