虚拟化 ItemsControl? ?

我有一个包含数据列表的 ItemsControl,我想虚拟化,但是 VirtualizingStackPanel.IsVirtualizing="True"似乎不与 ItemsControl工作。

真的是这样吗? 还是有其他我不知道的方法?

为了进行测试,我使用了以下代码块:

<ItemsControl ItemsSource="{Binding Path=AccountViews.Tables[0]}"
VirtualizingStackPanel.IsVirtualizing="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized"
Margin="5,50,5,50" Text="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

如果我将 ItemsControl改为 ListBox,我可以看到 Initialized事件只运行了少数几次(巨大的边距只是因为我只需要通过一些记录) ,然而作为一个 ItemsControl,每个项目得到初始化。

我已经尝试将 ItemsControlPanelTemplate设置为 VirtualizingStackPanel,但似乎没有帮助。

60728 次浏览

只是默认的 ItemsPanel不是 VirtualizingStackPanel,你需要改变它:

<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

实际上,它不仅仅是让 ItemsPanelTemplate使用 VirtualizingStackPanelItemsControl的默认 ControlTemplate没有 ScrollViewer,而 ScrollViewer是虚拟化的关键。添加到 ItemsControl的默认控制模板(使用 ListBox的控制模板作为模板)会得到以下结果:

<ItemsControl ItemsSource="{Binding AccountViews.Tables[0]}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Initialized="TextBlock_Initialized"
Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>


<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>


<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ScrollViewer CanContentScroll="True"
Padding="{TemplateBinding Padding}"
Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

(顺便说一句,查看默认控件模板的一个很好的工具是 给我看模板)

注意事项:

您必须设置 ScrollViewer.CanContentScroll="True",请参阅 给你了解原因。

还要注意,我把 VirtualizingStackPanel.VirtualizationMode="Recycling"。这将减少调用 TextBlock_Initialized的次数,无论屏幕上可以看到多少 TextBlock。你可以在这里阅读更多关于 UI 虚拟化的内容 .

编辑: 忘记说明一个显而易见的事实: 作为替代解决方案,您可以只用 ListBox:)替换 ItemsControl 另外,检查这个 优化 MSDN 页面的性能并注意到 ItemsControl不在“实现性能特性的控件”表中,这就是为什么我们需要编辑控件模板。

基于 DavidN 的回答,这里有一个你可以在 ItemsControl 上使用的虚拟化风格:

<!--Virtualised ItemsControl-->
<Style x:Key="ItemsControlVirtualizedStyle" TargetType="ItemsControl">
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True"
>
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

我不喜欢使用 ListBox 的建议,因为它们允许选择您不一定需要的行。