大型可滚动数据SL4的虚拟化性能问题

12

问题:在可滚动区域中显示大量数据时,性能和用户体验非常糟糕。

尝试过的方法:基本上,在 ListBox 中设置 DataTemplate 来显示填充数据的网格,并将 VirtualizationMode 设置为 Recycle,同时在 ListBox 自身上设置固定高度。就像下面的示例一样。

 <ListBox x:Name="Items"
      TabNavigation="Once"
      VirtualizingStackPanel.VirtualizationMode="Recycling"     
      Height="500">         
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <HyperlinkButton Content="Action" Margin="5"/>
                        <ContentControl  
                                cal:View.Model="{Binding}"  
                                VerticalContentAlignment="Stretch" 
                                HorizontalContentAlignment="Stretch"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

ContentControl将从另一个视图中引入一个标准的<Grid>,该视图格式化由大约20个静态和20个数据绑定TextBlock组成的填充项的整体布局。

这样可以正常工作,并将初始加载减半。然而,现在的问题是,我需要能够不固定高度,以便它占用父元素中可用的空间甚至可以调整大小。感谢@DanFox,我发现你必须以一种形式或另一种方式固定高度才能调用虚拟化,否则RenderEngine会认为它有无限的空间。

问题是:有更好的方法吗?或者至少如何修复当前技术以实现更好的用户体验?我正在生成数百个这些项,因此需要虚拟化的性能增强。但是我也需要允许用户调整窗口大小并保留有效滚动的能力。

任何见解都将不胜感激,谢谢和节日快乐!


你尝试过暂时固定ScrollViewer和其他组件的高度吗?有时性能下降可能是因为布局引擎给ScrollViewer一个无限高度。抱歉我不能更明确,我在这个领域有点生疏了,已经有一段时间没有做SL了... - Dan Fox
你的数据加载速度快吗?你绑定了哪种集合类型? - Big Daddy
@Dan Fox,那么仅仅硬性固定滚动视图的高度就会影响渲染速度吗? - Chris W.
@Big Daddy 数据检索似乎不是最令人担忧的问题,因为显示速度才是减慢程序的瓶颈。 - Chris W.
@DanFox 哦,我明白你的意思了。但是这是否真的像将父控件的高度设置为固定值那样容易实现呢?我还在考虑是否应该将ItemsControl替换为ListBox,并使用Virtualizingstackpanel作为项模板。 - Chris W.
显示剩余3条评论
5个回答

1

好的,这是我最终采取的措施,我必须说这是一个巨大的改进!我们开始时在这个特定部分的加载时间为77秒。通过对后端绑定方式进行一些重构,我们削减了约20秒的时间,因此问题仍然存在于UI渲染中。下面的答案显然比我最初想象的要简单得多,现在我们可以在15-20秒内加载大量数据(当然还有虚拟化),而较小的负载基本上是瞬间完成,使我们重新回到正轨。

这是我所做的:

 <!-- This needs to be contained in a parent panel like a grid -->
 <ListBox x:Name="Items" >
                <ListBox.Template>
                    <ControlTemplate> <!-- Broke it out to allow resizing -->
                        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                            <ItemsPresenter/> <!-- This little fella does magical things -->            
                        </ScrollViewer>         
                    </ControlTemplate>      
                </ListBox.Template>
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel VirtualizingStackPanel.VirtualizationMode="Recycling"/>  <!-- Recycle was a must -->        
                    </ItemsPanelTemplate>       
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <HyperlinkButton  VerticalAlignment="Top" Margin="5" />
                                <!-- This guy I did need to set a minwidth on to retain nice and predictable scrolling 
 when datacontext was potentially changing -->
                            <ContentControl cal:View.Model="{Binding}" MinWidth="1160"
                                            VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" />
                        </StackPanel>
                    </DataTemplate>         
                </ListBox.ItemTemplate>           
            </ListBox>

就是这样!瞧!只需要打破ControlTemplate并应用直接的ItemsPresenter!它现在可以很好地虚拟化,调整大小,平衡已经恢复到宇宙中,我不再想打一只独角兽了。之后,我只是剥离了视觉状态,因为我们不需要任何类型的项目选择,就这样,感谢大家的见解!很抱歉这次无法颁发奖励。


1

如您所请求 :-) 我觉得我到目前为止还没有“回答”任何问题...

您说得对,应该有一种方法来动态获取高度。将其设置为固定高度只是为了快速检查虚拟化是否正常工作。您在性能分析器方面有什么进展,或者使用SilverlightSpy?我通过重构UI/VM上的绑定使其中一个问题消失了,从而使其更有效率。WinPhone7 SL上有一个很好的资源,可能有一个很好的解决方案,我会看看能否找到它(理论应该转移到完整的SL)

这里提出了一个关于根据您的VM架构进行缓存的好观点

这里可能会有所帮助,因为它更详细地解释了懒加载

WinPho 7开发团队提供了一些提示

希望这能有所帮助。


0
如果只是因为固定高度的问题,为什么不试试这个呢:

<Grid>    
<ListBox x:Name="Items"
      TabNavigation="Once"
      VirtualizingStackPanel.VirtualizationMode="Recycling"     
      Height="{Binding Path=Height,RelativeSource={RelativeSource AncestorType=Grid}}">         
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <HyperlinkButton Content="Action" Margin="5"/>
                        <ContentControl  
                                cal:View.Model="{Binding}"  
                                VerticalContentAlignment="Stretch" 
                                HorizontalContentAlignment="Stretch"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
</Grid>

很重要的是,将ListBox放入适合外部空间而不是内部空间(如stackpanel)的容器中。

尝试一下,告诉我是否有帮助。:)


我可能错了,但我认为你不能在SL4中像这样使用RelativeSource?我认为它在SL5中得到了改进? - Dan Fox
丹是正确的,SL4只支持Self和TemplatedParent,AncestorType不行,这很遗憾,因为我经常遇到需要使用它的情况。不过在同一路线上,我尝试了Height={Binding ActualHeight, ElementName=ParentGridContainter},但也没有成功。我将一个文本块绑定到该属性,它返回一个完全无效的数字...但那是另一个话题了,哈哈。 - Chris W.
是的,很抱歉,AncestorType在SL5中是新的。尝试绑定到Height而不是ActualHeight。 - Johannes Wanzek
@inxs 嗯,我尝试了两种方法,但都没有达到预期的效果。为了进一步调查,我将一个文本块绑定到这两个属性上,它们都返回了预期的像素大小(比如30像素),并且渲染出来的高度只有应有的一小部分。然而,一旦视图完全渲染,它应该返回600+的数值,这真是个难题:/ - Chris W.
你确定你有正确的容器吗?30像素听起来像是你使用了错误的布局或容器? - Johannes Wanzek
你可以很容易地获取一个依赖项,给对象命名并将TextBlock的文本绑定到它上面,例如Text={Binding ActualHeight, ElementName=YourObjectName}。 - Chris W.

0

我觉得如果你在DataTemplate中神奇地拉取UI控件,那就违背了虚拟化的初衷。这些控件(网格)本身是否被重用?当{Binding}更改时会发生什么?在看起来像是附加行为的东西中运行了多少代码?也许你所做的可以变得更加高效。但是,如果你只是创建一个简单的DataTemplate(或将其放置在普通的UserControl中),你就知道其中所有的控件都将被重用,并且在切换数据上下文时将有最小的惩罚。

关于Height的问题,我不确定。它应该可以正常工作(在Arrange中获得有限的高度),但这取决于它在幕后执行的操作(也许在安排之前它正在尝试计算所有内容)。

您始终可以创建自己的虚拟化ItemsControl。从Panel派生,将其放置在ScrollViewer内部,并在Panel上实现IScrollInfo。然后你就会知道所有事情为什么会是这样了 :)


你也可以尝试直接在ScrollViewer中使用VirtualizingStackPanel。这可能是一种更好的强制虚拟化的方法。你也可以从VirtualizingStackPanel派生,这可能会给予从Panel派生的优势,但工作量较少。 - Aleksandr Dubinsky

0

我不确定这是否是您的使用情况可以接受的解决方案,但我创建了一个名为DelayedLayoutUpdateContainer的ContentControl,它包装了一个复杂的布局并帮助提高性能。 DelayedLayoutUpdateContainer背后的想法是,仅当其自身在给定时间跨度内未被调整大小时,它才会调整其内容的大小。 ControlTemplate中的ContentPresenter的大小设置为绝对值。因此,这可能具有将ListBoxes高度设置为绝对值的相同效果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接