Вы получите тот же эффект для не виртуализации ItemsControl
(ScrollViewer.CanContentScroll="False"
), что и для виртуализации, если прокрутить вниз, а затем выбрать верхний видимый контейнер с помощью мыши.Это также может быть сделано в коде.
Когда для CanContentScroll
установлено значение false, виртуализация отключена, поэтому все контейнеры будут создаваться всегда.Чтобы получить видимый сверху контейнер, мы можем перебирать контейнеры сверху, пока не достигнем VerticalOffset
из ScrollViewer
.Получив его, мы можем просто вызвать на нем BringIntoView
, и он будет хорошо выравниваться сверху, как если бы использовалась виртуализация.
Пример
<ListBox ItemsSource="{Binding MyCollection}"
ScrollViewer.CanContentScroll="False"
ScrollViewer.ScrollChanged="listBox_ScrollChanged" >
Вызовите BringIntoView
в верхнем видимом контейнере в обработчике событий
private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ItemsControl itemsControl = sender as ItemsControl;
ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer;
FrameworkElement lastElement = null;
foreach (object obj in itemsControl.Items)
{
FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > e.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
}
Чтобы добиться этого эффекта, только если вы хотите вызвать PageDown
, вНапример, нажав кнопку, вы можете создать метод расширения для ListBox
с именем LogicalPageDown
.
listBox.LogicalPageDown();
ListBoxExtensions
public static class ListBoxExtensions
{
public static void LogicalPageDown(this ListBox listBox)
{
ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox);
ScrollChangedEventHandler scrollChangedHandler = null;
scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) =>
{
scrollViewer.ScrollChanged -= scrollChangedHandler;
FrameworkElement lastElement = null;
foreach (object obj in listBox.Items)
{
FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > scrollViewer.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
};
scrollViewer.ScrollChanged += scrollChangedHandler;
scrollViewer.PageDown();
}
}
Я заметил в вашем вопросечто вы уже получили ScrollViewer
, но я добавляю реализацию к GetVisualChild
, если кто-то еще сталкивался с этим вопросом
public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}