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