Centering selected item in a scroll viewer

后端 未结 2 659
感动是毒
感动是毒 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

    
    
        
            
                
                    
                        
                            
                        
                    
                
            
    
            

    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
    {
        /// 
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// 
        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();
                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)this.listView.ItemsSource).Count());
            }
        }
    }
    

提交回复
热议问题