如何允许UWP ListView滚动浏览最后一项?

时间:2018-01-04 21:38:49

标签: uwp windows-10-universal

我有一个带有一堆不规则大小的项目的ListView。当您滚动ListView时,最后一项的底部将最终位于控件的底部:您无法继续滚动。

如果最后一项小于控件,我希望最后一项的 top 能够滚动到控件的顶部(如果项目大于控件,我使用默认行为很好;在这种情况下,项目的顶部将滚动到ListView控件的顶部,满足我的设计)。 (这类似于Visual Studio Code中的行为,editor.scrollBeyondLastLine设置为true)

几乎让这个有效但但它并不存在。在我的尝试中,我将ListView子类化,因此我可以覆盖PrepareContainerForItemOverride

然后我尝试为项目的下边距添加足够的额外空间,以便项目的顶部位于ListView的顶部。

  protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);

        var lvi = element as ListViewItem;
        if (lvi == null) { return; }

        var currentIndex = this.Items.IndexOf(item);
        if (currentIndex == (this.Items.Count - 1))
        {
            var sv = this.ScrollViewer();
            lvi.Measure(new Size(sv.ViewportWidth, double.PositiveInfinity));

            var slackSpace = sv.ViewportHeight - lvi.DesiredSize.Height;
            if (slackSpace > 0)
            {
                lvi.Margin = new Thickness(lvi.Margin.Left, lvi.Margin.Top, lvi.Margin.Right, slackSpace + lvi.Margin.Bottom);
            }                
        }
    }
}

(在此代码中,this.ScrollViewer()只是一个小的扩展方法,可以从任何ScrollViewer中找出ListView。我在这里尝试传递一些明智的东西到lvi.Measure。)

这并不是很有效,因为DesiredSize.Height的{​​{1}}最终比渲染ListViewItem的{​​{1}}大一点(我不喜欢#39;不知道为什么)。

如果我知道Height 总是大于需要的话,我实际上可以将这种行为视为妥协......但我敢打赌,这并不是真的如此。

所以我显然需要插入渲染管道的不同部分来管理它。但我无法弄清楚在哪里。

我是否应该以某种方式对ListViewItem DesiredSize.Height进行子类化(因为我认为ListView's实际上正在布置子ScrollViewer控件?)?还有其他方法可以做到这一点吗?还是只是不可能?

1 个答案:

答案 0 :(得分:1)

理想情况下,ScrollViewer在安排孩子(在这种情况下是面板)后添加额外的间距将是ScrollViewer的工作。仍然需要查看面板的最后一个子尺寸,以确切知道它应该添加多少空间。

但由于StackPanel被封存,我不知道这是否真的可能。

另一个Hack,就是创建一个自定义ScrollViewer's,我们可以在其中调整测量的高度,以便最后一个项目在顶部滚动。

底部的实际填充取决于ViewportHeight public class BottomPaddedStackPanel: StackPanel { private ScrollViewer scroll = null; public BottomPaddedStackPanel() { } protected override Size MeasureOverride(Size availableSize) { if (scroll == null) InitScrollViewerData(); Size panelSize = base.MeasureOverride(availableSize); var lastChild = this.Children[this.Children.Count - 1]; if (scroll == null || scroll.ViewportHeight == 0) return panelSize; // add more space at the bottom to be able to scroll the last item at the top if (lastChild.DesiredSize.Height < scroll.ViewportHeight) panelSize.Height += scroll.ViewportHeight - lastChild.DesiredSize.Height; return panelSize; } private void InitScrollViewerData() { var lastChild = this.Children[this.Children.Count - 1]; var lv = ItemsControl.ItemsControlFromItemContainer(lastChild); Border rootBorder = VisualTreeHelper.GetChild(lv, 0) as Border; scroll = rootBorder.Child as ScrollViewer; scroll.SizeChanged += (o, ev) => InvalidateMeasure(); } } ,因此多次通过度量布局步骤。

<ListView ItemsSource="{x:Bind Items}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <local:BottomPaddedStackPanel />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>

设置为列表的布局面板:

path