Виртуализация 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, но, похоже, это не помогает.


person Rachel    schedule 06.05.2010    source источник


Ответы (3)


На самом деле это гораздо больше, чем просто заставить ItemsPanelTemplate использовать VirtualizingStackPanel. По умолчанию ControlTemplate для ItemsControl не имеет 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>

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

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

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

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

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

person DavidN    schedule 06.05.2010
comment
Спасибо, это именно то, что я искал! Я искал поведение выбора, отличное от списка, и в то время думал, что проще всего сделать это с помощью элемента управления элементами. - person Rachel; 07.05.2010
comment
Если этот элемент управления дополнительно вложен, вы также должны указать ему высоту. В противном случае средство просмотра прокрутки не отображается. - person buckley; 25.09.2014
comment
Также обратите внимание, что я поставил VirtualizingStackPanel.VirtualizationMode = Recycling. Разве это не должно быть в предоставленном вами образце? - person buckley; 08.01.2015
comment
Работает ли виртуализация также при переносе ItemsControl в ScrollViewer, добавление Scroll в ControlTemplate? - person demo; 24.02.2016
comment
@DavidN Где и как я могу разместить заголовки столбцов в вашем решении? - person Ozkan; 30.11.2017
comment
Если я инкапсулирую ScrollViewer с VirtualizingStackPanel, тогда производительность резко упадет (я думаю, что виртуализация больше не работает) - person Ozkan; 30.11.2017
comment
«Показать мне шаблон» теперь является неработающей ссылкой - person vullnetyy; 16.11.2018
comment
По какой-то причине мой контент не прокручивается, когда я прокручиваю полосу прокрутки. Есть идеи, что может быть причиной этого? - person mrid; 05.08.2019
comment
Искал решение от 2 до 3 месяцев. Наконец, это решение, которое я хочу. Я заменил свой код на предложенный, и приложение работает без сбоев. Большое спасибо. - person Dhruv Panchal; 15.11.2019
comment
У ScrollViewer.CanContentScroll="True" есть один недостаток, который необходим для работы виртуализации: элемент вместо обработки на основе пикселей вызовет нежелательные пробелы в конце списка. Однако это можно исправить, начиная с .NET 4.5, добавив VirtualizingPanel.ScrollUnit="Pixel" VirtualizingPanel.IsContainerVirtualizable="True" в ItemsControl - person RonnyR; 14.08.2020

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

person Zodman    schedule 15.11.2012

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

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
person Abe Heidebrecht    schedule 06.05.2010
comment
Я не голосую, так как решение неполное. Для включения виртуализации в шаблоне необходимо использовать средство просмотра прокрутки. - person Saraf Talukder; 02.06.2015