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

时间:2012-12-14 04:27:05

标签: c# silverlight xaml uiscrollview datatemplate

问题:在可滚动区域中显示大量数据会导致性能和/或用户体验糟糕。

尝试:基本上在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只是认为它有无限的空间。

问题是:有没有更好的方法来做到这一点,或者我怎样才能至少修复当前的技术以实现更好的用户体验?我正在生成数百个这样的项目,所以我需要虚拟化的性能增强。但是,我还需要允许用户调整窗口大小并保持有效滚动的能力。

非常感谢任何见解,谢谢和节日快乐!

5 个答案:

答案 0 :(得分:1)

按要求:-)我觉得我到目前为止没有“回答”任何事情......

你是绝对正确的,应该有一种让身高变得动态的方法。将其设置为固定高度是一个快速检查,只是为了确保虚拟化正常工作。您是否可以使用性能分析器,或者使用SilverlightSpy?我通过重构UI / VM上的绑定使其更有效率,使其中一个问题消失了。 WinPhone7 SL有一个很好的资源,有一个潜在的好解决方案,我会看看我是否可以把它挖出来(理论应该转移到全脂SL)

This在缓存方面有一个很好的观点,具体取决于您的VM架构

This可能有用,因为它解释了laxy加载更多

来自WinPho 7开发团队的couple of hints

希望有所帮助

答案 1 :(得分: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!它现在虚拟化很好,它调整大小,平衡已经恢复到宇宙,我不再想要打一个独角兽。之后,我只是剥离了视觉状态,因为我们不需要任何项目选择,这就是它,感谢每个人的见解!对不起,我这次不能奖励。

答案 2 :(得分: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放入适合外部空间的容器中,而不是像堆栈面板那样适合内部空间。

试试这个并告诉我它是否有帮助。 :)

答案 3 :(得分:0)

我认为,如果你神奇地将UI控件纳入数据模板,那么你就无法实现虚拟化的目的。这些控件(网格)本身是否可以重复使用?当{Binding}改变时会发生什么?看起来像附加行为的代码运行了多少代码?也许你所做的事情可以做得很好。但是如果你只是制作一个直接的数据模板(或者将其放入一个普通的UserControl),你就会知道它中的所有控件都将被重用,并且在切换它们的datacontext时会有最小的惩罚。

我不确定Height的问题。它应该工作(在编排中得到一个有限的高度),但它取决于它在引擎盖下做了什么(也许它试图在安排使高度可用之前解决所有问题)。

您始终可以创建自己的虚拟化项目控件。从Panel派生,将其放入ScrollViewer,并在Panel上实现IScrollInfo。那么你就会知道事情就是这样的所有原因:)

答案 4 :(得分:0)

我不确定,如果这对您的用例来说是一个可接受的解决方案,但我创建了一个名为DelayedLayoutUpdateContainer的ContentControl,它包含复杂的布局并有助于提高性能。 DelayedLayoutUpdateContainer背后的想法是,只有当它在给定的时间跨度内没有自己调整大小时,它才会调整其内容的大小。 ControlTemplate中ContentPresenter的大小设置为绝对值。因此,这可能与将ListBoxes高度设置为绝对值具有相同的效果。