Coder1095
Coder1095

Reputation: 828

Performance degredation with custom ListBox ControlTemplate

Using a template for a custom control deriving from ListBox causes filtering of ItemSource to become slow. The filtering is done in the get of the ItemSource that the control is bound to. This problem is not present when a normal ListBox is used, so why should it be any different for a custom ListBox?

Filtering:

public IEnumerable<LibraryViewModel> Libraries {
    get {
        if (!string.IsNullOrEmpty(this.LibrarySearchString))
            return _libraries.Where(lib => IsLibraryMatch(lib, this.LibrarySearchString));
        else
            return _libraries.OrderBy(lib => !lib.IsFavourite);
    }
}  

Using the control:

<con:FilterListBox Grid.Row="1"
                   ItemsSource="{Binding Libraries}"
                   SelectedItem="{Binding SelectedLibrary}"
                   ItemTemplate="{StaticResource  
                                  LibraryItemTemplate}"                           
                   SearchString="{Binding LibrarySearchString, Mode=TwoWay}"
                   IsSearching="False"
                   Margin="4"/>

The control template:

<Style x:Key="{x:Type con:FilterListBox}" TargetType="{x:Type con:FilterListBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type con:FilterListBox}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <DockPanel Grid.Row="0">
                        <TextBlock Text="Search"
                                    VerticalAlignment="Center"/>
                        <TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                Path=SearchString,
                                                UpdateSourceTrigger=PropertyChanged}"
                                 Margin="4,0,0,0"/>
                    </DockPanel>

                    <ScrollViewer Grid.Row="1" CanContentScroll="True">
                        <StackPanel IsItemsHost="True"
                                    HorizontalAlignment="Stretch"/>
                    </ScrollViewer>

                    <TextBlock Grid.Row="1"
                               Text="Searching..."
                               HorizontalAlignment="Center"
                               VerticalAlignment="Center"
                               Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
                                                    Path=IsSearching,
                                                    Converter={StaticResource CollapsedIfFalseConverter}}"/>
                </Grid>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Thanks for any help.

Upvotes: 0

Views: 879

Answers (1)

JanW
JanW

Reputation: 1849

The slow behavior of your FilterListBox may come with an Virtualization issue. You replaced the ItemsHost of the ListBox with a simple StackPanel. By default, the ListBox uses a VirtualizingStackPanel, which virtualizes the Items whenever possible. See the default ListBox Template as a reference. If you have a simple StackPanel as ItemsPresenter, the ListBox has to re-render every item when your filter changes. Depending on the number of items, this can cause your slow behavior. Try to use the default itemshost instead. You should also know, that virtualization is only possible with 'simple' items (same Height for every item basically).

Upvotes: 4

Related Questions