问题
我们需要在 WPF ListBox 控件中高效地显示大量(>1000)的对象。 我们依靠 WPF ListBox 的虚拟化(通过 VirtualizingStackPanel)来高效地显示这些项。
错误:使用虚拟化时,WPF ListBox 控件不能正确显示项目。
如何重现
我们已将问题简化为下面独立的 xaml。
将 xaml 复制并粘贴到 XAMLPad 中。
最初,ListBox 中没有选定的项,因此所有项都是相同大小且完全填充可用空间,这是预期行为。
现在,点击第一项。 由于我们的 DataTemplate,所选项将展开以显示其他信息,这是预期行为。
正如预期的那样,这会导致水平滚动条出现,因为所选项现在比可用空间更宽。
现在使用鼠标单击并拖动水平滚动条向右。
错误:非选定的可见项不再拉伸以填充可用空间。所有可见项应具有相同的宽度。
这是一个已知的错误吗? 是否有任何途径可以通过 XAML 或编程方式修复此错误?
<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>