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

标签 wpf listbox scroll

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

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

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

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

最佳答案

如果向下滚动,然后选择非虚拟化 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;
}

关于wpf - 如何通过物理滚动滚动到列表框中的下一个逻辑页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7258387/

相关文章:

c# - 如何计算wpf中形状的方向

wpf - 需要有关在 WPF 中实现 UI 的建议

c# - 没有类型的 IEnumerable 属性

css - 滚动体*在*下方*一个透明的标题 div?

css - 为什么我的 div 在添加多张图片时没有水平滚动?

c# - 如何修复 Activator.CreateInstance 因 MissingMethodException "Constructor on type not found"而失败?

c# - 如何从 ViewModel 更新 View

c# - 为列表框中的每个项目加载不同的数据模板

c# - 在 ObservableCollection 上使用 CollectionViewSource 进行列表框实时排序

css - Bootstrap 3 : opened navbar's content can't scroll on mobile