Windows 10 ScrollIntoView () не прокручивает элементы в середине списка

У меня есть Listview с 20 элементами в нем. Я хочу прокручивать Listview программно.

ListView?.ScrollIntoView(ListView.Items[0]) 

прокрутит список до первого элемента.

 ListView?.ScrollIntoView(ListView.Items.Count - 1) 

прокрутит список в нижней части страницы.

Тем не менее, я не могу использовать одну и ту же функцию для прокрутки списка к элементу посередине.

 Eg: ListView?.ScrollIntoView(ListView.Items[5]) 

должен прокрутить и перенести меня в пятый элемент списка. Но вместо этого он ведет меня к первому пункту списка.

Было бы здорово, если это поведение может быть достигнуто с помощью некоторого обходного пути?

Я думаю, что вы ищете метод, чтобы прокрутить элемент вверху ListView .

В этом сообщении я создал метод расширения, который прокручивается к определенному элементу в ScrollViewer .

Идея в вашем случае такая же.

Вам нужно сначала найти экземпляр ScrollViewer в вашем ListView , а затем фактический элемент для прокрутки до, то есть ListViewItem .

Ниже приведен метод расширения для получения ScrollViewer .

 public static ScrollViewer GetScrollViewer(this DependencyObject element) { if (element is ScrollViewer) { return (ScrollViewer)element; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) { var child = VisualTreeHelper.GetChild(element, i); var result = GetScrollViewer(child); if (result == null) { continue; } else { return result; } } return null; } 

Как только я получу экземпляр ScrollViewer , я создал еще два метода расширения для прокрутки к элементу на основе его индекса или прикрепленного объекта соответственно. Поскольку ListView и GridView используют один и тот же базовый class ListViewBase . Эти два метода расширения также должны работать для GridView .

Обновить

В основном, методы сначала найдут элемент, если он уже отображен, затем прокрутите его прямо сейчас. Если элемент имеет значение null , это означает, что виртуализация включена, и элемент еще не реализован. Итак, чтобы сначала реализовать элемент, вызовите ScrollIntoViewAsync (метод на основе задач, чтобы обернуть встроенный ScrollIntoView , такой же, как ChangeViewAsync , который предлагает гораздо более чистый код), вычислить позицию и сохранить ее. Поскольку теперь я знаю позицию для прокрутки, мне нужно сначала прокрутить элемент до последней позиции (т.е. без анимации), а затем, наконец, прокрутить до нужной позиции с анимацией.

 public async static Task ScrollToIndex(this ListViewBase listViewBase, int index) { bool isVirtualizing = default(bool); double previousHorizontalOffset = default(double), previousVerticalOffset = default(double); // get the ScrollViewer withtin the ListView/GridView var scrollViewer = listViewBase.GetScrollViewer(); // get the SelectorItem to scroll to var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem; // when it's null, means virtualization is on and the item hasn't been realized yet if (selectorItem == null) { isVirtualizing = true; previousHorizontalOffset = scrollViewer.HorizontalOffset; previousVerticalOffset = scrollViewer.VerticalOffset; // call task-based ScrollIntoViewAsync to realize the item await listViewBase.ScrollIntoViewAsync(listViewBase.Items[index]); // this time the item shouldn't be null again selectorItem = (SelectorItem)listViewBase.ContainerFromIndex(index); } // calculate the position object in order to know how much to scroll to var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content); var position = transform.TransformPoint(new Point(0, 0)); // when virtualized, scroll back to previous position without animation if (isVirtualizing) { await scrollViewer.ChangeViewAsync(previousHorizontalOffset, previousVerticalOffset, true); } // scroll to desired position with animation! scrollViewer.ChangeView(position.X, position.Y, null); } public async static Task ScrollToItem(this ListViewBase listViewBase, object item) { bool isVirtualizing = default(bool); double previousHorizontalOffset = default(double), previousVerticalOffset = default(double); // get the ScrollViewer withtin the ListView/GridView var scrollViewer = listViewBase.GetScrollViewer(); // get the SelectorItem to scroll to var selectorItem = listViewBase.ContainerFromItem(item) as SelectorItem; // when it's null, means virtualization is on and the item hasn't been realized yet if (selectorItem == null) { isVirtualizing = true; previousHorizontalOffset = scrollViewer.HorizontalOffset; previousVerticalOffset = scrollViewer.VerticalOffset; // call task-based ScrollIntoViewAsync to realize the item await listViewBase.ScrollIntoViewAsync(item); // this time the item shouldn't be null again selectorItem = (SelectorItem)listViewBase.ContainerFromItem(item); } // calculate the position object in order to know how much to scroll to var transform = selectorItem.TransformToVisual((UIElement)scrollViewer.Content); var position = transform.TransformPoint(new Point(0, 0)); // when virtualized, scroll back to previous position without animation if (isVirtualizing) { await scrollViewer.ChangeViewAsync(previousHorizontalOffset, previousVerticalOffset, true); } // scroll to desired position with animation! scrollViewer.ChangeView(position.X, position.Y, null); } public static async Task ScrollIntoViewAsync(this ListViewBase listViewBase, object item) { var tcs = new TaskCompletionSource(); var scrollViewer = listViewBase.GetScrollViewer(); EventHandler viewChanged = (s, e) => tcs.TrySetResult(null); try { scrollViewer.ViewChanged += viewChanged; listViewBase.ScrollIntoView(item, ScrollIntoViewAlignment.Leading); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; } } public static async Task ChangeViewAsync(this ScrollViewer scrollViewer, double? horizontalOffset, double? verticalOffset, bool disableAnimation) { var tcs = new TaskCompletionSource(); EventHandler viewChanged = (s, e) => tcs.TrySetResult(null); try { scrollViewer.ViewChanged += viewChanged; scrollViewer.ChangeView(horizontalOffset, verticalOffset, null, disableAnimation); await tcs.Task; } finally { scrollViewer.ViewChanged -= viewChanged; } } 

Более простой подход, но без анимации

Вы также можете использовать новую перегрузку ScrollIntoView , указав второй параметр, чтобы убедиться, что элемент выровнен по верхнему краю; однако при этом мои предыдущие методы расширения не имеют плавного перехода прокрутки.

 MyListView?.ScrollIntoView(MyListView.Items[5], ScrollIntoViewAlignment.Leading); 

ScrollIntoView просто приносит элемент в представление, период, он не прокручивается до строки.

Если вы вызываете его на члене, и он находится ниже нижней части видимого списка, он прокручивается до тех пор, пока элемент не станет последним членом в видимом списке.

Если вы назовете его членом, и он находится над верхней частью списка, он прокручивается до тех пор, пока элемент не станет первым членом в списке.

Если вы вызываете его на члене, и он в настоящее время виден, он вообще не выполняет никаких операций.

Я решаю это как:

  var sv = new ScrollViewerHelper().GetScrollViewer(listView); sv.UpdateLayout(); sv.ChangeView(0, sv.ExtentHeight, null); 

И метод GetScrollViewer:

 public ScrollViewer GetScrollViewer(DependencyObject element) { if (element is ScrollViewer) { return (ScrollViewer)element; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) { var child = VisualTreeHelper.GetChild(element, i); var result = GetScrollViewer(child); if (result == null) { continue; } else { return result; } } return null; } 

кредиты владельцу кода

  • Связывание RelativeSource с помощью ToolTip или ContextMenu
  • WPF: слайдер с событием, которое запускается после того, как пользователь перетаскивает
  • Как заставить детей StackPanel заполнить максимальное пространство вниз?
  • Почему обертки свойств .NET обойдены во время выполнения при настройке свойств зависимостей в XAML?
  • Являются ли "{Binding Path =.}" И "{Binding}" действительно равными
  • Связывание видимости DataGridTextColumn
  • Как извлечь шаблон управления по умолчанию в Visual Studio?
  • Привязать к методу в WPF?
  • Применение инсульта к текстовому блоку в WPF
  • DataTrigger, где значение НЕ равно нулю?
  • ResourceDictionary в отдельной сборке
  • Давайте будем гением компьютера.