Search code examples
wpfxamltreeviewvirtualizationcontroltemplate

TreeView loses virtualization with overridden control template


When I have the following TreeView in XAML (ItemsSource is set via code-behind to a long list of lists of lists):

<TreeView x:Name="Tree"
          VirtualizingPanel.IsVirtualizing="True"
          ScrollViewer.CanContentScroll="True">
    <!-- ItemTemplate and ItemContainerStyle ommitted for brevity -->
</TreeView>

virtualization works just fine. However, when I tried overriding the TreeView's template, along with the inner ScrollViewer's template, virtualization disappeared. My template seems identical to the default, with the exception of a colored rectangle that I took out which was the entire motivation for redefining the template.

    <TreeView.Template>
        <ControlTemplate TargetType="{x:Type TreeView}">
            <ScrollViewer Focusable="False" 
                          CanContentScroll="True">
                <ScrollViewer.Template>
                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition Height="auto" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="auto" />
                            </Grid.ColumnDefinitions>

                            <ScrollContentPresenter Grid.Column="0" Grid.Row="0" />

                            <ScrollBar x:Name="PART_VerticalScrollBar" 
                                       Orientation="Vertical"
                                       Grid.Row="0" Grid.Column="1"
                                       Value="{TemplateBinding VerticalOffset}"
                                       Maximum="{TemplateBinding ScrollableHeight}"
                                       ViewportSize="{TemplateBinding ViewportHeight}"
                                       Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />

                            <ScrollBar x:Name="PART_HorizontalScrollBar"
                                       Orientation="Horizontal"
                                       Grid.Row="1" Grid.Column="0"
                                       Value="{TemplateBinding HorizontalOffset}"
                                       Maximum="{TemplateBinding ScrollableWidth}"
                                       ViewportSize="{TemplateBinding ViewportWidth}"
                                       Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
                        </Grid>
                    </ControlTemplate>
                </ScrollViewer.Template>
                <ScrollViewer.Content>
                    <ItemsPresenter x:Name="ItemsPresenter" />
                </ScrollViewer.Content>
            </ScrollViewer>
        </ControlTemplate>

I've temporarily gotten around this by just making a transparent SolidColorBrush named {x:Static SystemColors.ControlBrushKey} in the TreeView's resources, but I'd prefer to know what I've done incorrectly.


Solution

  • I found the problem. In the ScrollViewer template, the ScrollContentPresenter needs to have CanContentScroll set to its templated parent's value.

    <ScrollContentPresenter Grid.Column="0" Grid.Row="0"
                            CanContentScroll="{TemplateBinding CanContentScroll}" />