133

我有一个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为 a ListBox,我可以看到该Initialized事件只运行了几次(巨大的边距只是让我只需要浏览几条记录),但是ItemsControl每个项目都被初始化。

我尝试将其设置ItemsControlPanelTemplate为 aVirtualizingStackPanel但这似乎没有帮助。

4

3 回答 3

236

实际上,它不仅仅是ItemsPanelTemplate使用VirtualizingStackPanel. 的默认值ControlTemplate没有ItemsControlScrollViewer这是虚拟化的关键。添加到默认控件模板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>

(顺便说一句,查看默认控件模板的好工具是Show Me The Template

注意事项:

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

还要注意我把VirtualizingStackPanel.VirtualizationMode="Recycling". TextBlock_Initialized这将减少调用的次数,但在屏幕上可以看到许多 TextBlock。您可以在此处 阅读有关 UI 虚拟化的更多信息。

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

于 2010-05-06T20:25:14.470 回答
44

基于 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 的建议,因为它们允许选择您不需要的行。

于 2012-11-15T06:00:57.613 回答
-4

只是默认ItemsPanel值不是VirtualizingStackPanel. 你需要改变它:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
于 2010-05-06T20:15:16.470 回答