Виртуализация ItemsControl?

125

У меня 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>

Если я изменю на ItemsControla ListBox, я вижу, что Initializedсобытие запускается всего несколько раз (огромные поля просто так, что мне нужно пройти только несколько записей), однако при ItemsControlинициализации каждого элемента.

Я пытался установить значение ItemsControlPanelTemplatea, VirtualizingStackPanelно это, похоже, не помогает.

Рейчел
источник

Ответы:

219

На самом деле это гораздо больше, чем просто 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ее нет в таблице «Элементы управления, реализующие функции производительности», поэтому нам нужно отредактировать шаблон управления.

DavidN
источник
1
Спасибо, это именно то, что я искал! Я искал поведение выбора, отличное от списка, и в то время думал, что проще всего будет использовать элемент управления элементами.
Рэйчел
Если этот элемент управления дополнительно вложен, вы также должны указать ему высоту. В противном случае средство просмотра прокрутки не отображается.
Buckley
9
«Также обратите внимание, что я поставил VirtualizingStackPanel.VirtualizationMode = Recycling». Разве это не должно быть в предоставленном вами образце?
Buckley
Есть ли виртуализация работать , когда обертка ItemsControlв ScrollViewerinstread добавления Scrollк ControlTemplate?
демо
@DavidN Где и как я могу разместить заголовки столбцов в вашем решении?
Озкан
37

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

Zodman
источник
-3

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

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Абэ Хайдебрехт
источник
8
Я не голосую, так как решение неполное. Для включения виртуализации в шаблоне необходимо использовать средство просмотра прокрутки.
Сараф Талукдер