Centering selected item in a scroll viewer

后端 未结 2 648
感动是毒
感动是毒 2021-01-03 01:49

I am trying to center a selected item in a ListView inside a ScrollViewer and struggling to calculate the vertical offset that I should be setting the ScrollViewer relative

相关标签:
2条回答
  • 2021-01-03 02:16

    Try ListView.ScrollIntoView() or ListView.MakeVisible first to scroll the container of the item into view and work around it being possibly virtualized out of the UI. Then use ListView.ItemContainerGenerator.ContainerFromIndex() to get the container of the item and then the VisualTreeHelper to get its position relative to the ScrollViewer. Then scroll the scrollviewer by the calculated offset.

    *EDIT - Example positioning logic:

    Get the VisualTreeHelperExtensions from WinRT XAML Toolkit to get access to the ScrollViewer easily with GetFirstDescendantOfType() extension method that wraps some calls to the 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());
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-03 02:39

    Yup, this worked for me out of the box with ScrollIntoView

        private void CountKeyBoard_NavigationEvent(object sender, Controls.UserControls.NavigationEventArgs e)
        {
            if (lvInvDetails.SelectedItem != null && lvInvDetails.SelectedIndex != -1)
            {
                switch (e.Direction)
                {
                    case Controls.UserControls.NavigationEventArgs.DirectionEnum.NEXT:
                        if (lvInvDetails.SelectedIndex >= (lvInvDetails.Items.Count - 1))
                            lvInvDetails.SelectedIndex = 0;
                        else
                            lvInvDetails.SelectedIndex++;
                        break;
    
                    case Controls.UserControls.NavigationEventArgs.DirectionEnum.PREVIOUS:
                        if (lvInvDetails.SelectedIndex > 0)
                            lvInvDetails.SelectedIndex--;
                        else
                            lvInvDetails.SelectedIndex = lvInvDetails.Items.Count - 1;
                        break;
                }
                lvInvDetails.ScrollIntoView(lvInvDetails.SelectedItem);
            }
        }
    
    0 讨论(0)
提交回复
热议问题