The Problem

This is a problem I see often, sometimes in on-line tutorials, and in code I come across from time to time. The problem is thinking you have item virtualisation when, in fact, you do not. Or, as a beginner with XAML, you may not be aware of virtualisation yet.

Firstly though, what is Collection Virtualisation?
It’s a very simple idea. If you were to have a large list of data to display on your UI, a list that extends well beyond the boundaries of the UI; Virtualisation is a mechanism which relieves pressure on the rendering work by not looking a items which the user cannot see. Point of fact, it actually extends a little further than what the user can see; in what can be referred to as the Realisation Window. If you were to have a vertical list, spanning the height of your UI, this Realisation Window extends one UI height up, and one UI height down.

So… Back to the problem. The problem is often caused my optimisation attempts made in the XAML. This attempts are usually found when replacing an ListBox with an ItemsControl. Why might you want to do this?

This is a very valid thing to do. The ItemsControl found in all XAML frameworks is a very light control compared to it’s derived relatives; the ListBox, ListView etc. You may rightly chose to use a simple ItemsControl to show a list of items you do not intend to select or interactive with; well, not without making considerable changes to the ItemsTemplate.

However, doing so comes at a cost…

The first of which is very obvious, and that is you can no longer scroll your list of items in any direction; the ScrollViewer is not present in the template of the ItemsControl. Actually, there’s pretty much nothing in there except the ItemsPresenter needed to show the items.

Consider the following…

<ItemsControl ItemsSource="{Binding Items}">

The above is a basic setup of the ItemsControl bound to it’s source of ‘Items’. This will not provide with a mechanism to scroll through your list of items.

<ScrollViewer>
 <ItemsControl ItemsSource="{Binding Items}">
</ScrollViewer>

And this is a potential fix to this problem. (This isn’t a good way to do this, and at the end of this piece we’ll look at a bit of code that shows us where the ScrollViewer should go.)

Let’s take a closer look at the above two snippets of code. I’ve done this in a simple WPF application so I can make use of an older tool which I still like; the ‘WPF Inspector’. It can be found here… wpfinspector.codeplex.com

In my application, the property the ItemsControl is bound to is an Array of Byte, and I’ve quickly populated this with random values, with a length of 500. When I run this application and attach the WPF inspector I see this curious statement at the bottom.

ListVirtualisationWithScroller

Our list is not being virtualised. This means the rending engine is doing far more work than it needs to. For this application the overhead is not noticeable; but if our items were quite complicated and shown in an application which had other UI concerns to deal with; we’ll very quickly see a degraded performance when this list is rendered.

So what happens if we add the ScrollViewer to our visual tree as shown in the code snippet earlier?

ListVirtualisationWithScroller

Other than now being able to scroll the list vertically, there was no change to the state of virtualisation. Note the number of UI elements being rendered, which is next to ‘MainWindow’, that states 1034.

If we were to make the ItemsControl a ListBox and remove the ScrollViewer in our code like this…

<ListBox ItemsSource="{Binding Items}">

Then run the application and attach the WPF Inspector, we’ll find two things. Firstly the warning about our virtualisation has disappeared, and; the UI elements will be much lower than the 1034 mentioned in the last image. I’m showing 102 next to MainWindow now, this may vary depending on how high your MainWindow is.

What is the first thing this shows us? It shows us that the ListBox is doing far more than what its visual appearance lets us believe. Even though dropping to an ItemsControl has removed the Selector and the UI elements making up the list box, our application performance is potentially in danger; which is the opposite effect we intended when choosing an ItemsControl over a ListBox.

However, not all is lost. We can build this functionality back into our ItemsControl with a little extra XAML.

Consider the next piece of code…

<ItemsControl ItemsSource="{Binding Items}">

<ItemsControl.ItemsPanel>
  <ItemsPanelTemplate>
    <VirtualizingStackPanel/>
  </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<ItemsControl.Template>
   <ControlTemplate TargetType="ItemsControl">
     <ScrollViewer>
        <CanContentScroll="True">
      <ItemsPresenter/>
    </ScrollViewer>
  </ControlTemplate>
</ItemsControl.Template>

</ItemsControl>

Run the application and once again attach the WPF Inspector. You’ll find our changes have made an impact on the virtualisation of our list, and peace is resorted to the land of XAML.

I hope this helps as when I started working with XAML some years ago I pulled out a few hairs wondering why my UI was slower when I’d remove UI elements from the tree. If you have any questions please find me on twitter @GLanata.

Advertisements