Problem
We need to efficiently display a large (>1000) number of objects in a WPF ListBox control. We are relying on the WPF ListBox’s virtualization (via VirtualizingStackPanel) to efficiently display these items.
Bug: The WPF ListBox control does not display items correctly when using virtualization.
How to Reproduce
We have distilled the problem to the standalone xaml shown below.
Copy and paste the xaml into XAMLPad.
Initially, there is no selected item in the ListBox, so as expected, all items are the same size and they completely fill the available space.
Now, click on the first item. As expected, because of our DataTemplate, the selected item will expand to show additional information.
As expected, this causes the horizontal scrollbar to appear, since the selected item is now wider than the available space.
Now use the mouse to click and drag the horizontal scrollbar to the right.
Bug: the non-selected visible items no longer stretch to fill the available space. All the visible items should be the same width.
Is this a known bug? Is there any way to fix this, either via XAML or programmatically?
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>
<DataTemplate x:Key="MyGroupItemTemplate">
<Border Background="White"
TextElement.Foreground="Black"
BorderThickness="1"
BorderBrush="Black"
CornerRadius="10,10,10,10"
Cursor="Hand"
Padding="5,5,5,5"
Margin="2"
>
<StackPanel>
<TextBlock Text="{Binding Path=Text, FallbackValue=[Content]}" />
<TextBlock x:Name="_details" Visibility="Collapsed" Margin="0,10,0,10" Text="[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}"
Value="True">
<Setter Property="TextElement.FontWeight"
TargetName="_details"
Value="Bold"/>
<Setter Property="Visibility"
TargetName="_details"
Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Page.Resources>
<DockPanel x:Name="LayoutRoot">
<Slider x:Name="_slider"
DockPanel.Dock="Bottom"
Value="{Binding FontSize, ElementName=_list, Mode=TwoWay}"
Maximum="100"
ToolTip="Font Size"
AutoToolTipPlacement="BottomRight"/>
<!--
I want the items in this ListBox to completly fill the available space.
Therefore, I set HorizontalContentAlignment="Stretch".
By default, the WPF ListBox control uses a VirtualizingStackPanel.
This makes it possible to view large numbers of items efficiently.
You can turn on/off this feature by setting the ScrollViewer.CanContentScroll to "True"/"False".
Bug: when virtualization is enabled (ScrollViewer.CanContentScroll="True"), the unselected
ListBox items will no longer stretch to fill the available horizontal space.
The only workaround is to disable virtualization (ScrollViewer.CanContentScroll="False").
-->
<ListBox x:Name="_list"
ScrollViewer.CanContentScroll="True"
Background="Gray"
Foreground="White"
IsSynchronizedWithCurrentItem="True"
TextElement.FontSize="28"
HorizontalContentAlignment="Stretch"
ItemTemplate="{DynamicResource MyGroupItemTemplate}">
<TextBlock Text="[1] This is item 1." />
<TextBlock Text="[2] This is item 2." />
<TextBlock Text="[3] This is item 3." />
<TextBlock Text="[4] This is item 4." />
<TextBlock Text="[5] This is item 5." />
<TextBlock Text="[6] This is item 6." />
<TextBlock Text="[7] This is item 7." />
<TextBlock Text="[8] This is item 8." />
<TextBlock Text="[9] This is item 9." />
<TextBlock Text="[10] This is item 10." />
</ListBox>
</DockPanel>
</Page>