简体   繁体   中英

Virtualization Performance Issue with Large Scrollable Data SL4

Problem: Displaying large amounts of data in a scrollable area has horrible performance and/or User eXperience.

Tried: Basically set a DataTemplate in a ListBox to show a grid of populated data with the VirtualizationMode set to Recycle and a fixed height set on the ListBox iteself. Something like the example below.

 <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>

The ContentControl would be bringing in a standard <Grid> from another view that formats the overall layout of the populated items consisting of around 20 static, and 20 data bound TextBlocks.

This works alright, and cut the initial loads in half. HOWEVER, now the problem is I need the ability for the height to NOT be a fixed size so it takes up the space available in its parent and can even be resized. Thanks to @DanFox I found out you have to fix the height in one form or another to invoke the virtualizing or the RenderEngine just thinks it has infinite room anyway.

The Question is: Is there a better way to do this, or how can I at least fix the current technique to allow for better UX? I'm generating potentially hundreds of these items so I need the performance enhancement of virtualization. However I also need to allow the user to resize the window and retain the ability to scroll effectively.

Any insight is greatly appreciated, thanks and Happy Holidays!

as requested :-) I didn't feel like I'd "answered" anything so far...

You're absolutely right, there should be a way of getting the height dynamic. Setting it to be fixed height is a quick check just to make sure the virtualization is working. Did you get anywhere with the performance profiler, or maybe using SilverlightSpy? I have made one of these issues go away by refactoring the binding on the UI/VM to make it more efficient. There was a good resource for WinPhone7 SL on this with a potentially good solution, I'll see if I can dig it out (the theory should transfer to full-fat SL)

This has a good point on caching depending on your VM architecture

This might be useful as it explains laxy loading a little more

And a couple of hints from the WinPho 7 dev team.

Hope that helps

Ok so here's what I ended up doing and have to say it's a huge improvement! We started with a load time of 77seconds on the dot for this particular piece. With some refactoring on how we were binding on the back we shaved about 20 seconds off, so the problem was still in the UI rendering. The answer below is obviously more simple than I originally thought it would be and now we're loading gigantic amounts of data in 15-20 seconds (with virtualization of course) and smaller loads are basically instantaneous putting us back on track.

Here's what I did;

 <!-- 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>

And that's it! Voila! All it took was to break out the ControlTemplate and apply a direct ItemsPresenter! It now virtualizes great, it resizes, balance has been restored to the universe and I no longer want to punch a unicorn. After that I just stripped the visual states because we didn't need any kind of item selection and that's about it, thanks for everyones insight! Sorry I couldn't award a bounty this time.

If its only due to the fixed height, why dont you just try this:

<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>

Important that you the ListBox into a container that fits to outer space and not to inner space like a stackpanel.

Try this and tell me if it helps. :)

I think if you're magically pulling in UI controls into your datatemplate, you're defeating the purpose of virtualization. Are these controls (grids) themselves being reused? What happens when the {Binding} changes? How much code is run in what looks like an attached behavior? Perhaps what you've done could be made performant. But if you just make a straight-forward datatemplate (or put in it an ordinary UserControl), you'll know all the controls in it will be reused and there will be minimum penalty in switching their datacontext.

I'm not sure about the issue with Height. It should work (get a finite height in Arrange), but it depends on what's it doing under the hood (perhaps it's trying to figure everything out before Arrange makes the height available).

You can always create your own virtualizing itemscontrol. Derive from Panel, put it inside a ScrollViewer, and implement IScrollInfo on the Panel. Then you'll know ALL the reasons why things are the way they are :)

I'm not sure, if that would be an acceptable solution for your use case, but I've created a ContentControl named DelayedLayoutUpdateContainer that wraps a complex layout and helps improving performance. The idea behind the DelayedLayoutUpdateContainer is, that it adjusts the size of its content only when it has not been resized itself for a given time span. The size of the ContentPresenter inside the ControlTemplate is set to absolute values. So that may have the same effect as setting the ListBoxes height to an absolute value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM