Wpf 如何通过物理滚动在列表框中滚动到下一个逻辑页

Wpf 如何通过物理滚动在列表框中滚动到下一个逻辑页,wpf,listbox,scroll,Wpf,Listbox,Scroll,我有一个列表框,它必须有CanContentScroll==false,因为我需要能够平滑地滚动它。这将启用物理滚动 我还想逐页滚动列表框,但是如果我在listbox internal ScrollViewer上调用PageDown方法,第一行将被剪切,因为列表框高度不是行高度的倍数 我希望第一行始终完全可见,就像使用逻辑滚动时一样 有人能告诉我怎么做吗?如果你将“新页面”的顶部项目滚动到视图中,这会达到你想要的效果吗? 看 如果向下滚动,然后用鼠标选择上方可见的容器,则非虚拟化项控件(Scro

我有一个列表框,它必须有CanContentScroll==false,因为我需要能够平滑地滚动它。这将启用物理滚动

我还想逐页滚动列表框,但是如果我在listbox internal ScrollViewer上调用PageDown方法,第一行将被剪切,因为列表框高度不是行高度的倍数

我希望第一行始终完全可见,就像使用逻辑滚动时一样


有人能告诉我怎么做吗?

如果你将“新页面”的顶部项目滚动到视图中,这会达到你想要的效果吗?

如果向下滚动,然后用鼠标选择上方可见的容器,则非虚拟化
项控件(
ScrollViewer.CanContentScroll=“False”
)的效果与虚拟化控件相同。这也可以在代码中完成

CanContentScroll
设置为false时,虚拟化将关闭,因此将始终生成所有容器。要获得顶部可见的容器,我们可以从顶部迭代容器,直到到达
ScrollViewer
VerticalOffset
。一旦我们得到了它,我们就可以简单地在它上面调用
BringIntoView
,它将在顶部很好地对齐,就像使用虚拟化一样

范例


为了仅在您想要调用
PageDown
时实现此效果,例如,在按钮单击中,您可以为
ListBox
创建一个名为
logicalpage down
的扩展方法

listBox.LogicalPageDown();
ListBoxExtensions


我认为该项目将显示在列表框的底部,而不是顶部。另外,我也不知道如何识别该项目。@Zmaster:忘记了在查找容器的相对位置时可能涉及到
边距
等。在我决定使用ScrollToOffset()时更新了我的答案,方法是在假设每个项目都具有相同高度的情况下计算目标偏移量(这是我的情况)。它比迭代项目更有效。另一方面,您的解决方案也适用于不同高度的项目,因此我接受它作为答案。
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;
    }
}
listBox.LogicalPageDown();
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();
    }
}
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;
}