如何允许UWP列表视图滚动到最后一项?

如何允许UWP列表视图滚动到最后一项?,uwp,windows-10-universal,Uwp,Windows 10 Universal,我有一个列表视图,其中有一堆大小不规则的项目。滚动ListView时,最后一项的底部将位于控件的底部:您不能继续滚动 如果最后一个项目小于控件,我希望最后一个项目的顶部能够滚动到控件的顶部(如果项目大于控件,我对默认行为没有意见;在这种情况下,项目的顶部将滚动超过满足我设计的ListView控件的顶部)。(这类似于将editor.scrollBeyondLastLine设置为true的Visual Studio代码中的行为) 我几乎让它工作了,但它不在那里。在我的尝试中,我对ListView进行

我有一个列表视图,其中有一堆大小不规则的项目。滚动ListView时,最后一项的底部将位于控件的底部:您不能继续滚动

如果最后一个项目小于控件,我希望最后一个项目的顶部能够滚动到控件的顶部(如果项目大于控件,我对默认行为没有意见;在这种情况下,项目的顶部将滚动超过满足我设计的ListView控件的顶部)。(这类似于将editor.scrollBeyondLastLine设置为true的Visual Studio代码中的行为)

我几乎让它工作了,但它不在那里。在我的尝试中,我对
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()
只是一个小小的扩展方法,它从任何
ListView
中筛选出
ScrollViewer
。我在这里使用它试图将一些合理的信息传递给
lvi.Measure

这不太管用,因为
ListViewItem
DesiredSize.Height
最终会比渲染的
ListViewItem
高度大一点(我不知道为什么)

如果我知道
DesiredSize.Height
总是比需要的大,那么我实际上可以接受这种行为作为一种妥协……但我敢打赌事实并非如此

因此,我显然需要插入渲染管道的不同部分来管理它。但我不知道在哪里


我是否应该以某种方式对
ListView的
ScrollViewer
进行子类化(因为我认为
ScrollViewer
实际上是子
ListViewItem
控件的布局?)?还有别的办法吗?还是根本不可能

理想情况下,
ScrollViewer的工作是在安排好子对象(本例中为面板)后添加额外的间距。不过,它还需要查看面板的最后一个子尺寸,才能确切知道它应该添加多少空间

但是由于ScrollViewer是密封的,我不知道这是否真的可能

另一个技巧是创建一个自定义的
StackPanel
,在这里我们可以调整测量的高度,以便最后一个项目可以滚动到顶部

底部的实际填充取决于ScrollViewer的
视口高度
,因此需要多次通过测量布局步骤

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();
    }
}
public类BottomPaddedStackPanel:StackPanel
{
私有ScrollViewer scroll=null;
公共底部填充StackPanel()
{
}
受保护的覆盖尺寸测量覆盖(尺寸可用尺寸)
{
如果(滚动==null)
InitScrollViewerData();
尺寸面板尺寸=基本尺寸(可用尺寸);
var lastChild=this.Children[this.Children.Count-1];
if(scroll==null | | scroll.ViewportHeight==0)
返回面板尺寸;
//在底部添加更多空间,以便能够滚动顶部的最后一项
if(lastChild.DesiredSize.HeightInvalidateMeasure();
}
}
设置为列表的布局面板:

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


你能用一些视觉效果来加深理解吗?这太完美了。现在我学到了更多关于ItemsPanel和ItemsPanelTemplate的知识。非常感谢。