虚拟化ItemsControl?

时间:2010-05-06 19:29:03

标签: wpf virtualization itemscontrol virtualizingstackpanel

我有一个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,但似乎没有帮助。

3 个答案:

答案 0 :(得分:201)

实际上,除了ItemsPanelTemplate使用VirtualizingStackPanel之外,还有更多内容。 ControlTemplate的默认ItemsControl没有ScrollViewer,这是虚拟化的关键。添加到ItemsControl的默认控件模板(使用ListBox的控件模板作为模板)为我们提供了以下内容:

<ItemsControl
    VirtualizingStackPanel.IsVirtualizing="True"
    ScrollViewer.CanContentScroll="True"
    ItemsSource="{Binding Path=AccountViews.Tables[0]}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock
                Initialized="TextBlock_Initialized"
                Text="{Binding Path=Name}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
        <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>
    </ItemsControl.Template>
</ItemsControl>

(BTW,一个查看默认控件模板的好工具是Show Me The Template

需要注意的事项:

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

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

编辑:忘了陈述显而易见的:作为替代解决方案,您只需将ItemsControl替换为ListBox :) 另外,请查看此Optimizing Performance on MSDN page并注意ItemsControl不在“实施性能功能的控件”表中,这就是我们需要编辑控件模板的原因。

答案 1 :(得分:29)

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

答案 2 :(得分:-3)

默认ItemsPanel不是VirtualizingStackPanel。你需要改变它:

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