我确实有一个列表框,它必须有 CanContentScroll==false 因为我需要能够平滑地滚动它。这可以实现物理滚动。
我还想按页滚动列表框,但如果我在列表框内部 ScrollViewer 上调用 PageDown 方法,第一行会被剪切,因为列表框高度不是行高的倍数。
我希望第一行始终完全可见,就像使用逻辑滚动时一样。
有人可以给我一个关于如何做到这一点的提示吗?
最佳答案
如果向下滚动,然后选择非虚拟化 ItemsControl
(ScrollViewer.CanContentScroll="False"
),您将获得与虚拟化控件相同的效果用鼠标显示上部可见容器。这也可以通过代码完成。
当 CanContentScroll
设置为 false 时,虚拟化将关闭,因此所有容器都将始终生成。要获取顶部可见容器,我们可以从顶部迭代容器,直到到达 ScrollViewer
的 VerticalOffset
。获得它后,我们可以简单地对其调用 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;
}
关于wpf - 如何通过物理滚动滚动到列表框中的下一个逻辑页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7258387/