When we display large numbers of items in our collection controls, it can negatively affect the application's performance. This is because the layout system will create a layout container, such as a ComboBoxItem in the case of a ComboBox, for example, for every item in the data bound collection. As only a small subset of the complete number of items is displayed at any one time, we can take advantage of virtualization to improve the situation.
UI virtualization defers the generation and layout of these item containers until each item is actually visible in the relevant collection control, often saving on large amounts of resources. We can take advantage of virtualization without doing anything at all if we use ListBox or ListView controls to display our collections, as they use it by default.
Virtualization can also be enabled in ComboBox, ContextMenu, and TreeView controls, although it will have to be done manually. When using a TreeView control, we can enable virtualization by simply setting the VirtualizingStackPanel.IsVirtualizing Attached Property to True on it:
<TreeView ItemsSource="{Binding Items}" VirtualizingStackPanel.IsVirtualizing="True" />
For other controls that use the StackPanel class internally, such as the ComboBox and ContextMenu controls, we can enable virtualization by setting an ItemsPanelTemplate element hosting an instance of the VirtualizingStackPanel class with its IsVirtualizing property set to True to its ItemsPanel property:
<ComboBox ItemsSource="{Binding Items}"> <ComboBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel IsVirtualizing="True" /> </ItemsPanelTemplate> </ComboBox.ItemsPanel> </ComboBox>
Apart from setting the IsVirtualizing property to False, there are a few other reasons why UI virtualization may not work. One case is when item containers have manually been added to an ItemsControl object or one of its derived controls. Another case is when the item containers are of different types.
The final reason why virtualization may not work is not so obvious and relates to the CanContentScroll property of the ScrollViewer class. This is an interesting property that specifies whether the ScrollViewer in a collection control will scroll its items in logical units or physical units. The default value is False, which smoothly scrolls in terms of physical units.
Physical units relate to the device-independent pixels that WPF works with, while logical units relate to the widths or heights of the collection items, depending on the orientation of the control. As the default value of the CanContentScroll property is False, this will need to be set to True to enable virtualization. This is so that scrolling is performed item by item and not pixel by pixel.
When virtualization is employed in a collection control that extends the ItemsControl class and the user scrolls, new item containers are created for the newly visible items and the containers for the items that are no longer visible are disposed of.
In version 3.5 of the .NET Framework, an optimization of the virtualization system was introduced. Container recycling enables the collection control to reuse the item containers, instead of creating new ones and disposing of old ones as the user scrolls. This offers an additional performance benefit and can be enabled by setting the VirtualizationMode Attached Property to a value of Recycling:
<TreeView ItemsSource="{Binding Items}" VirtualizingStackPanel.IsVirtualizing="True" /> VirtualizingStackPanel.VirtualizationMode="Recycling" />
One further optimization that WPF provides us with is deferred scrolling. Normally, scrolling in a collection control continuously updates the UI. However, if our data items or their item containers have several layers of visuals that define them and scrolling is slow, we can opt to defer the UI update until scrolling has finished.
In order to enable deferred scrolling on a collection control, we need to set the ScrollViewer.IsDeferredScrollingEnabled Attached Property to True. Although we don't generally use ScrollViewer elements in XAML directly, we can also attach this property to collection controls that host a ScrollViewer element in their control templates:
<ListBox ItemsSource="{Binding Items}"
ScrollViewer.IsDeferredScrollingEnabled="True" />
We've now investigated performance improvements that we can make with computer hardware, resources, correct control selection, methods of drawing and displaying images, outputting text, linking, data binding, minimizing memory footprints, and data virtualization. There is just one more essential area to look at, that is, events, so let's look at that next.