Lately I was working on an application that had to display a large amount of objects on screen and allow filtering. I have learned that scrolling large collections was not so simple in WPF, and I definitely did not see the problems coming.

 

Memory Consumption

 

Here is an example of a simple WPF application which randomly creates 50000 Classes of type DATA in a list and displays them in an Item control:

 

Here is the Xaml of the application:

   1: <Window x:Class="PredicatePerformanceTest.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         xmlns:my="clr-namespace:PredicatePerformanceTest"
   5:     Title="Window1" Height="500" Width="200">
   6:     <Window.Resources>
   7:         <DataTemplate DataType="{x:Type my:Data}">
   8:             <Border BorderBrush="Black" BorderThickness="1" Width="100" 
   9:                     CornerRadius="4" x:Name="brd">
  10:                 <Label Content="{Binding MyProperty}" FontSize="14" 
  11:                        Foreground="Blue">
  12:                 </Label>
  13:             </Border>
  14:             <DataTemplate.Triggers>
  15:                 <DataTrigger Binding="{Binding IsPositive}" Value="True">
  16:                     <Setter TargetName="brd" Property="Border.BorderBrush" 
  17:                             Value="DarkGreen"></Setter>
  18:                     <Setter TargetName="brd" Property="Border.Background" 
  19:                             Value="Green"></Setter>
  20:                 </DataTrigger>
  21:                 <DataTrigger Binding="{Binding IsPositive}" Value="False">
  22:                     <Setter TargetName="brd" Property="Border.BorderBrush" 
  23:                             Value="DarkRed"></Setter>
  24:                     <Setter TargetName="brd" Property="Border.Background" 
  25:                             Value="Red"></Setter>
  26:                 </DataTrigger>
  27:             </DataTemplate.Triggers>
  28:         </DataTemplate>
  29:     </Window.Resources>
  30:     <ScrollViewer CanContentScroll="True">
  31:         <ItemsControl ItemsSource="{Binding}" >            
  32:         </ItemsControl>
  33:     </ScrollViewer>
  34: </Window>

 

And the Code Behind:

   1: public partial class Window1 : Window
   2:     {
   3:         List<Data> m_ints = new List<Data>();
   4:         public Window1()
   5:         {
   6:             InitializeComponent();
   7:             CreateList();
   8:             this.DataContext = m_ints;
   9:         }
  10:  
  11:         private void CreateList()
  12:         {
  13:             Random r = new Random();
  14:             for (int i = 0; i < 50000; i++)
  15:             {
  16:                 m_ints.Add(new Data() { MyProperty = r.Next(-1000, 1000) });
  17:             }
  18:         }
  19:     }
  20:  
  21:     public class Data
  22:     {
  23:         public int MyProperty { get; set; }
  24:         public bool IsPositive
  25:         {
  26:             get
  27:             {
  28:                 if (MyProperty > 0)
  29:                 {
  30:                     return true;
  31:                 }
  32:                 else
  33:                 {
  34:                     return false;
  35:                 }
  36:             }
  37:         }
  38:     }

Very Simple. If the integer is positive, the border and fill are green, if its negative its red.

 

Here is a screen shot of the Task Manager once this application is up and running:

WPF Memory Consumption

 

A Whopping 500MB of RAM!!! I thought to myself that it is because I am holding 50000 Class objects and I was wrong. Here is the task manager right after the List Creation:

WPF MEmory Usage

 

Barely 7MB of Ram. This means that drawing the Classes takes 500MB of ram. Be careful when you are working with Large Collection.

 

Scrolling Performance and Panel Virtualization

 

If you would run the previous example you will notice that the scrolling performance is terrible. It is not smooth and scrolls in Blocks, there is a small overhead in memory consumption, The reason it is not smooth is that every time you scroll new objects are created to show the data.

 

If the Templates are complicated you will notice a spike in the Memory Consumption. Here the charge is small but I have seen applications that simply by scrolling you add 50% to the Memory consumption of the application, not very nice…

 

You can solve this easily by ordering WPF to recycle objects, just like in real life :).

all you have to do is add two attributes to the items control:

   1: <ItemsControl ItemsSource="{Binding}" VirtualizingStackPanel.IsVirtualizing="True" 
   2:               VirtualizingStackPanel.VirtualizationMode="Recycling">            
   3: </ItemsControl>

No more Wasting of good Templates that have already been created, and a big improvement to performance and Memory Consumption.

 

So be careful when binding to large collections.

 

And don’t forget to recycle!

 

Amit

Tags :

6 Responses to “Things to Notice When Binding to Large Collection”


  1. Vijay Santhanam

    Said on January 21, 2009 :

    wow, that sounds horrible! how do u get around that problem in production?

  2. Amit

    Said on January 21, 2009 :

    I am currently trying to think of a solution, something like drawing only the items that are currently visible and not drawing the ones that are at the bottom of the list.

    But I have found out another thing about large collection and it is related to sorting and filtering, check out my next post.

    Amit

  3. mycall

    Said on February 18, 2009 :

    I just wrote a caching .NET 2.0 solution that basically caches 40MB of text files into memory (as List). The lists take up about 150MB of memory. Each list has over 100,000 items. Packing the items slows things a lot. So do binary trees. Good thing memory is cheap these days (unless you are using a 32-bit OS).

  4. Botsutoshi

    Said on February 19, 2009 :

    I’ve exactly copied the code described above, but my performances don’t change. Even I set the VirtualizingStackPanel attributes, my application continues to eat a lot of ram. Situation changes if I replace the ItemsControl with a ListBox. Any suggestion? Where is my error?

  5. Amit

    Said on February 19, 2009 :

    @botsutoshi

    I have written another post that is a “second part” to this one and explains the problem, check it out here:

    http://www.dev102.com/2009/02/09/why-the-itemscontrol-scrollviewer-attached-property-does-not-work/

    Let me know if it helps

  6. Botsutoshi

    Said on February 19, 2009 :

    AAAAAAAAAaaaaaaaaaaaaaaaaah ok! :D

Post a Comment