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

发布于 2024-12-02 05:02:41 字数 220 浏览 0 评论 0原文

我确实有一个列表框,它必须有 CanContentScroll==false 因为我需要能够平滑地滚动它。这使得物理滚动成为可能。

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

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

有人可以给我一个关于如何做到这一点的提示吗?

I do have a listbox which must have CanContentScroll==false because i need to be able to scroll it smoothly. This enables physical scrolling.

I also want to scroll the listbox by page, but if i call the PageDown method on the listbox internal ScrollViewer the first row gets cutted because the listbox height is not a multiple of the row height.

I want the first row to be always completely visible, like when using logical scrolling.

Can someone give me a hint on how to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小巷里的女流氓 2024-12-09 05:02:41

如果向下滚动,然后选择上方的可见项,您将获得与虚拟化控件相同的非虚拟化 ItemsControl (ScrollViewer.CanContentScroll="False") 效果容器与鼠标。这也可以通过代码完成。

CanContentScroll 设置为 false 时,虚拟化将关闭,因此所有容器都将始终生成。要获取顶部可见容器,我们可以从顶部迭代容器,直到到达 ScrollViewerVerticalOffset。获得它后,我们可以简单地对其调用 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;
}

You'll get the same effect for a non virtualizing ItemsControl (ScrollViewer.CanContentScroll="False") as for a virtualizing one if you scroll down and then select the upper visible container with the mouse. This can also be done in code.

When CanContentScroll is set to false, virtualizing is turned off so all containers will be generated at all times. To get the top visible container we can iterate the containers from the top until we reach the VerticalOffset of the ScrollViewer. Once we got it we can simply call BringIntoView on it and it will align nicely at the top just like it would if virtualization was being used.

Example

<ListBox ItemsSource="{Binding MyCollection}"
         ScrollViewer.CanContentScroll="False"
         ScrollViewer.ScrollChanged="listBox_ScrollChanged" >

Call BringIntoView on the top visible container in the event handler

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;
    }
}

To only achieve this effect when you want to call PageDown, in a Button click for example, you can create an extension method for ListBox called 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();
    }
}

I noticed in your question that you already got the ScrollViewer but I'm adding an implementation to GetVisualChild if anyone else comes across this question

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;
}
最丧也最甜 2024-12-09 05:02:41

如果您将“新页面”的顶部项目滚动到视图中,是否会实现您想要的效果?
请参阅 ScrollIntoView< /a>.

If you scroll the top item of the "new page" into view, would that achieve what you're looking for?
See ScrollIntoView.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文