When putting together the code for the Descry - Social Timeline visualization, I quickly discovered that when I subscribed to FriendFeed friends that contained large numbers of items, I eventually encountered performance issues. Scobleizer’s feed was a great acid test, since he had about 13K+ subscribers. We needed the Social Timeline to display, at a maximum, 3 months of subscriber data. I found as the total number of FriendFeed items approached 10K, scrolling and animating databound StackPanels became unacceptably slow. My opinion is that quick response time not only is a critical determining factor in user satisfaction, but also contributes to faster user feedback time and continuity, so that user can make better informed decisions.
It can be argued that what I was attempting to do was an architectural design flaw, since we were attempting to use StackPanels to represent one days’ worth of stacked FriendFeed items; a total of 93 StackPanels for 3 months of data. However, we wished to retain the resizing and scaling properties of the StackPanels’ parent Grid control and it’s ColumnDefinitions, so that we would could avoid writing messy sizing/positioning logic, that the Grid control, conveniently, already has built-in. As a result we would need to find another method to gain performance. We ended up choosing control virtualization, since the sheer number of FriendFeed items is what was causing the slowness.
So, what do I mean by “virtualized control?” A virtualized control is one that utilizes resources efficiently. You might ask: “Shouldn’t all controls utilize resources efficiently?” The answer to that is yes, but there are performance and behavior tradeoffs between a typical control and a virtualized control. Non-virtualized controls have all the databound UI already in memory. Virtualized controls are specialized controls geared for managing large amounts of data that need to bind UI on the fly, and they take a performance hit when doing this. Also, you’ll note that when scrolling the content of the virtual StackPanel control in this project that it does not smoothly scroll up a pixel as a time as a normal ListBox does, instead it scrolls up one item at a time. This is not a limitation of a virtualized control, but rather a limitation of this implementation. To implement this would take a bit more thought.
In other words, when a typical Silverlight control is databound to a data source that contains ten’s of thousand objects, it will literally contain ten’s of thousand UI elements in its’ visual tree; potentially using a vast amount of resources. The visual tree is maintained in memory (in addition to the original data source) whether the visual tree is onscreen or off! Conversely, a virtualized control while having access the to the data source in memory, would only contain the elements that need to be physically displayed within the viewable area of the control, resulting in a dramatically smaller set of items in the visual tree.
This sample shows a virtualization technique for creating a scalable and performant ListBox-like control. There are two key classes: the VirtualizedStackPanel and the VirtualizedItemsControl. The source for this simple virtualized control is here.
The VirtualizedStackPanel is a control that simply limits the number of children contained in its visual tree to only the items in the viewable area. This control alone is unable to display the data properly, since a scrollbar is needed to manage the position of the currently viewable area; the VirtualizedItemsControl takes care of that need.
The VirtualizedStackPanel behaves as a normal StackPanel until a data generator is attached. The IDataGenerator implemented in the custom VirtualizedItemsControl lets the VirtualizedStackPanel know what children are currently in the active viewable area, based on control size and scrollbar position settings. As soon as the size of the control, or scrollbar position changes, the MeasureOverride method is called, and the control recalculates which children are to be added or removed from the visual tree. A ContentPresenter is acquired from the assigned data generator, so that a common DataTemplate can be used to define item UI. When the ContentPresenter Content property is set, the data is bound to the UI defined in the DataTemplate.
The main logic in the VirtualizedStackPanel is in the MeasureOverride method, creates new containers if they are needed when the control height increases, or reuses containers if no new ones are needed, and also removes unnecessary containers of the control height decreases. The ArrangeOverride simply positions all the children in the StackPanel vertically.
The VirtualizedItemsControl’s role is to act as a container for one or more Virtualized StackPanel’s. It manages the data source, a common DataTemplate that children use, and it also manages the Scrollbar positioning logic. The VirtualizedItemsControl implements the IDataGenerator interface that assists in communicating with its VirtualizedStackPanel children. This sample shows a single VirtualizedStackPanel child for brevity. But you may refer to the VirtualizedTimelineItemsControl in the Social Timeline project on CodePlex for one that contains multiple children.
Incidentally, the VirtualizedItemsControl’s content is defined in the projects /Themes/Generic.xaml file, it could have been just as well defined procedurally, but it uses an external resource file for better designer / developer workflow. It is critical that this file exist in the project in the Themes folder, and note that this file needs to be defined as a resource in the project to avoid errors. I made the Silverlight 2 newbie mistake of not doing this, and I literally spent days trying to figure out why I couldn't get the project to work.
I found that writing a virtualized control wasn’t quite as complex as I had anticipated, after I broke down a much more complex sample. There is a good example of a full Implementation of a VirtualListBox in the Silverlight Extensions project on CodePlex. Another related WPF VirtualizedCanvas sample by Chris Lovett that I found useful is located here.
Descry and Codeplex
Visit CodePlex to download a more fully developed version of a virtualized control that supports mouse drag animation with physics, plus a VirtualizedListBox as well. And visit here to get the entire Descry project source!