Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 543
Chris W. Avatar asked Dec 14 '12 04:12

Chris W.


2 Answers

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

like image 96
Dan Fox Avatar answered Nov 04 '22 12:11

Dan Fox


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.

like image 38
Chris W. Avatar answered Nov 04 '22 12:11

Chris W.