I'm building an application that displays a hierarchy of Node
objects. Each node has a collection of Field
objects, plus a Children
property of type ObservableCollection<Node>
.
I've created a user control to display the Node
objects. This control is a HeaderedItemsControl
whose header lays out all of the Field
objects, and whose ItemsSource
is the Children
property. It's all nicely hierarchical.
The problem is in performance. Each node can have any number of children, and the hierarchy can go any number of levels deep. The test data set I'm working with has about 120 nodes and 2800 fields all told. This takes about 15-20 seconds to render once the underlying view model objects are created. It's clear that this is because it's creating visuals for every one of those field objects, even those (about 90% of them) that don't appear on the screen.
The obvious solution is to employ virtualization. But this:
</HeaderedItemsControl.Header>
<HeaderedItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</HeaderedItemsControl.ItemsPanel>
</HeaderedItemsControl.Header>
appears to have no effect; it renders just as slowly as it does when I use a StackPanel
or a DockPanel
. I think this is because the panel can't decide whether or not it should virtualize the first item until it knows how tall it is, and to do that it has to go all the way to leaf nodes. But I don't understand why it has to create a visual for every single leaf node in order to do this.
I'm using this simple control template (and why is it, pray tell, that a HeaderedItemsControl
doesn't render anything until you set its control template?):
<Style TargetType="HeaderedItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedItemsControl}">
<DockPanel>
<ContentPresenter ContentSource="Header" DockPanel.Dock="Top"/>
<ItemsPresenter Margin="10,0,0,0"/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Amusingly, if I use a VirtualizingStackPanel
instead of a DockPanel
in the control template, it doesn't render at all!
I could conceivably use the approach that Bea Stollnitz suggests, of flattening out the hierarchy and displaying it using a regular ItemsControl
and indenting. That makes for a lot of code, though, particularly if I want to make some (or all) nodes in the tree collapsible.
As a practical matter, I doubt that I'll ever have an actual real-world case where I'm rendering a hierarchy of nodes that's this big. But it would be nice to have a strategy for what happens when what I doubt will happen happens. Is there another way?