在滚动查看器中居中选定的项目

时间:2014-03-23 19:09:50

标签: c# windows-runtime windows-store-apps winrt-xaml

我正在尝试将一个选定的项目放在ScrollViewer内的ListView中,并努力计算我应该相对于ListView设置ScrollViewer的垂直偏移。

以下链接让我走上了正轨,但由于WinRT API的限制,我们无法使用它们:

期望的效果如下:

centered selected item in scrollviewer

这是我的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);
}

1 个答案:

答案 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());
        }
    }
}