我正在尝试将一个选定的项目放在ScrollViewer内的ListView中,并努力计算我应该相对于ListView设置ScrollViewer的垂直偏移。
以下链接让我走上了正轨,但由于WinRT API的限制,我们无法使用它们:
期望的效果如下:
这是我的XAML中的示例设置:
<ScrollViewer x:Name="MyScrollViewer">
<ListView x:Name="MyView" VerticalAlignment="Center"
SelectionChanged="Selector_OnSelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="80" Height="80" Margin="0">
<TextBlock Text="{Binding}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Items>
<x:String>1</x:String>
<x:String>2</x:String>
<x:String>3</x:String>
<x:String>4</x:String>
<x:String>5</x:String>
<x:String>6</x:String>
<x:String>7</x:String>
<x:String>8</x:String>
<x:String>9</x:String>
</ListView.Items>
</ListView>
</ScrollViewer>
知道所选项目的索引,如何计算我可以在我的方法中使用的垂直偏移量:
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
double maxVerticalOffset = MyScrollViewer.ExtentHeight - MyScrollViewer.ViewportHeight;
int selectedItemIndex = MyView.SelectedIndex;
double verticalOffset = ...
MyScrollViewer.ChangeView(null, verticalOffset, null);
}
答案 0 :(得分:6)
首先尝试ListView.ScrollIntoView()
或ListView.MakeVisible
将项目的容器滚动到视图中,然后解决它可能会从UI虚拟化。然后用
ListView.ItemContainerGenerator
.
ContainerFromIndex
()获取项目的容器,然后VisualTreeHelper
获取相对于ScrollViewer
的位置。然后按计算的偏移量滚动滚动查看器。
*编辑 - 示例定位逻辑:
从WinRT XAML工具包中获取VisualTreeHelperExtensions,以ScrollViewer
扩展方法轻松访问GetFirstDescendantOfType()
,该方法包含对VisualTreeHelper
的一些调用。
<强> XAML 强>
<Page
x:Class="ListViewItemCentering.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ListViewItemCentering"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView
x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<Border
Width="400"
Height="100">
<ContentControl
Content="{Binding}"
FontSize="48"
Padding="20,10"/>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
Content="Skip"
Width="200"
Height="100"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Click="ButtonBase_OnClick"/>
</Grid>
</Page>
<强> C#强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using WinRTXamlToolkit.Controls.Extensions;
namespace ListViewItemCentering
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private Random random = new Random();
public MainPage()
{
this.InitializeComponent();
this.listView.ItemsSource = Enumerable.Range(1, 1000);
this.listView.SelectionChanged += OnListViewSelectionChanged;
}
private async void OnListViewSelectionChanged(object sender, SelectionChangedEventArgs selectionChangedEventArgs)
{
if (listView.SelectedItem == null)
{
return;
}
var item = listView.SelectedItem;
// Calculations relative to screen or ListView
var listViewItem = (FrameworkElement)listView.ContainerFromItem(item);
if (listViewItem == null)
{
listView.ScrollIntoView(item);
}
while (listViewItem == null)
{
await Task.Delay(1); // wait for scrolling to complete - it takes a moment
listViewItem = (FrameworkElement)listView.ContainerFromItem(item);
}
var topLeft =
listViewItem
.TransformToVisual(listView)
.TransformPoint(new Point()).Y;
var lvih = listViewItem.ActualHeight;
var lvh = listView.ActualHeight;
var desiredTopLeft = (lvh - lvih) / 2.0;
var desiredDelta = topLeft - desiredTopLeft;
// Calculations relative to the ScrollViewer within the ListView
var scrollViewer = listView.GetFirstDescendantOfType<ScrollViewer>();
var currentOffset = scrollViewer.VerticalOffset;
var desiredOffset = currentOffset + desiredDelta;
scrollViewer.ScrollToVerticalOffset(desiredOffset);
// better yet if building for Windows 8.1 to make the scrolling smoother use:
// scrollViewer.ChangeView(null, desiredOffset, null);
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
this.listView.SelectedIndex = random.Next(0, ((IEnumerable<int>)this.listView.ItemsSource).Count());
}
}
}