WPF 4.5 – Part 11 & 12 : new features for the VirtualizingPanel

, , 13 Comments

Virtualizing panels are involved when an application’s performance needs to be improved. It provide a base panel for virtualization of children elements inside it. Instead of creating all the UI element which are inside an ItemControls, only the one which need to be displayed are created. Because the process of creating these elements is intensive, the use of a VirtualizingPanel makes an item controls display faster.

The VirtualizingPanel comes with new features in WPF 4.5: two properties named ScrollUnit and CacheLength and virtualization on grouped data. In this post we will discover them in details.

This post is a part of a serie on WPF 4.5 new features.

WPF 4.0 behavior

To illustrate the new properties and their usage, we will create a demo project. It can be found on my Dropbox folder after registration.
This simple WPF application display a list of persons in a listbox with virtualization enabled. In the last part, we will group the persons by age.

A button is here to allow the user to add a lot of persons to the list (500 actually). When this operation is done, we calculate the elapsed render time and display it as a status bar message. This allow us to be sure that virtualization is really enabled. When it is, the load takes a dozen of ms and when it’s not, the operation takes a lot of time (7000 ms on my computer). This is one of the scenarii addressed by the virtualization.
[csharp]foreach (var person in persons)
{
Persons.Add(person);
}
var watch = new Stopwatch();
watch.Start();
//Wait for the rendering is finished..
Dispatcher.CurrentDispatcher.Invoke(new Action(() => { }),
DispatcherPriority.Loaded, null);

watch.Stop();

Message = string.Format("Rendering took {0} ms.", watch.ElapsedMilliseconds);
[/csharp]

Virtualization can be enabled and disabled via the VirtualizingStackPanel.IsVirtualizing attached property on a ListBox. By default it is but we force it anyway:
[xml]<ListBox ItemsSource="{Binding Persons}" Background="LightBlue"
ItemTemplate="{StaticResource PersonDataTemplate}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
VirtualizingStackPanel.IsVirtualizing="True"
HorizontalContentAlignment="Stretch" />
[/xml]

To be able to see things clearly, I defined a style for the ListBoxItem which set their background to pink and I set the ListBox’s Background to blue:

CacheLength

The ViewPort is the visible aera of the virtualizing panel. With virtualization, only visible inside the viewport are really created in memory. Creating this items can be intensive and requires a noticeable time to be done. If so, the scrolling experience is not very good for the user because the reactivness of the application falls to nothing.

To reproduce this scenario, I have create an UserControl named TimeConsumingControl and I do something long in its constructor. Then I put it in the DataTemplate:
[xml]<DataTemplate x:Key="PersonDataTemplate"
DataType="{x:Type local:Person}">
<Grid>
<!–This will take time to be instancied–>
<local:TimeConsumingControl />

<!–Rest of the data template–>

</Grid>
[/xml]

In WPF 4.5 you can create a Cache of the item not displayed. When the virtualizing panel has finished the render of its items, it start the creation of the cache with a low priority. By doing so, when the user scroll into the list, he’ll not feel the slowness of the rendering because the items will already be created in the cache. Not that it’ll be true only for the one in the cache.

You can use two properties to configure the cache in WPF 4.5:

  1. CacheLength : The amount of space created in the cache before and after the ViewPort. The default value is 1,1.
  2. CacheLengthUnit : the unit of the ammount of space: Pixel, Item or Page. A page is defined by the size of the viewort. The default value is Page in the MSDN doc but it seems to be Item in my tests.

To demonstrate the benefit of their use is hard to do with a picture, but you can try the demo application to notice it.

Here is an example of their use in the the demo application XAML which define a cache of 2 pages before the viewPort and 3 after it:
[xml]<ListBox ItemsSource="{Binding Persons}"
ItemTemplate="{StaticResource PersonDataTemplate}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.CacheLength="2,3"
VirtualizingStackPanel.CacheLengthUnit="Page"/>
[/xml]

ScrollUnit

When virtualization is enabled, the scrolling can be perceived as little odd by the user because only full items are displayed. If an item does not fit entirely in the viewport defined by the ItemsControl then it’s not displayed.

In WPF 4.5, you can set the ScrollUnit property to one of these following values:

  • Item : The virtualizing panel can display blank aera because items that would require to be crop are not displayed. This is the default value (as in WPF 4.0).
  • Pixel : The virtualizing panel can display partial / cropped items as you may excepted it to be the default behavior.

So if we update the previous example to set this property to Pixel we’ll get the following XAML:
[xml]<ListBox ItemsSource="{Binding Persons}"
ItemTemplate="{StaticResource PersonDataTemplate}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.ScrollUnit="Pixel" />
[/xml]

Virtualization when grouping

In WPF 4.0, you lost virtualization when grouping is done on the collection you display. I repeat : Grouping = no virtualization in WPF 4.0. This is still the default behavior of WPF 4.5 but you can turn on the virtualization by using the IsVirtualizingWhenGrouping attached property of the VirtualizingPanel class. When this is done, you benefit of all the already described advantages of virtualization.

Here is how you can enable it via XAML:
[xml] <ListBox ItemsSource="{Binding Persons}"
ItemTemplate="{StaticResource PersonDataTemplate}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">
<ListBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}" />
</ListBox.GroupStyle>
</ListBox>
[/xml]

IsContainerVirtualizable

Finally, I found out reading the MSDN documentation a new attached property named IsContainerVirtualizable. It’s seems to be a property which tells the VirtualizingPanel if it should virtualize an item or not.

I tried to use it but I didn’t managed to. So I can’t tell you more on this except that it exists !

Regards,

 

13 Responses

  1. kets

    16/03/2012 3 h 48 min

    I am trying to download your code but I am unable to download it as its taking me to dropbox registration page inspite of installing dropbox. Please can you help me in it? Thanx in advance.

  2. Genti

    03/04/2013 17 h 06 min

    Very nice article.
    I was wondering if there is a way to specify the ScrollUnit in WinRT?
    I cannot find such property on the VirtualizingStackPanel.

  3. Olivier Albertini

    13/07/2015 1 h 43 min

    Salut,

    Les codes relatifs à au poste sur : serie on WPF 4.5 new features, ne sont plus disponibles
    Serait-il possible de les repartager ?

    Mille merci d'avance

  4. Anugeeta

    17/06/2016 17 h 03 min

    Hi

    Very nice article.
    But I am not able to open the link to demo project

    Anugeeta

Comments are closed.