Виртуализация ItemsControl? - PullRequest
117 голосов
/ 06 мая 2010

У меня есть 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 ]

201 голосов
/ 07 мая 2010

На самом деле это гораздо больше, чем просто 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>

(Кстати, отличный инструмент для просмотра шаблонов управления по умолчанию - Show Me The Template )

На что обратить внимание:

Вы должны установить ScrollViewer.CanContentScroll="True", см. здесь , почему.

Также обратите внимание, что я поставил VirtualizingStackPanel.VirtualizationMode="Recycling". Это уменьшит количество раз, когда вызывается TextBlock_Initialized, однако многие текстовые блоки видны на экране. Подробнее о виртуализации пользовательского интерфейса можно прочитать здесь .

РЕДАКТИРОВАТЬ: Забыл заявить очевидное: в качестве альтернативного решения, вы можете просто заменить ItemsControl на ListBox :) Кроме того, ознакомьтесь с этой Оптимизацией производительности на странице MSDN и обратите внимание, что ItemsControl отсутствует в таблице «Элементы управления, реализующие функции производительности», поэтому нам нужно отредактировать шаблон элемента управления.

29 голосов
/ 15 ноября 2012

Основываясь на ответе 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, так как они позволяют выбирать строки там, где вы этого не хотите.

0 голосов
/ 07 мая 2010

Просто по умолчанию ItemsPanel не VirtualizingStackPanel. Вы должны изменить это:

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