Виртуализация 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
но это, похоже, не помогает.
3 ответа
Там на самом деле гораздо больше, чем просто сделать 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
отсутствует в таблице "Элементы управления, реализующие функции производительности", поэтому нам нужно отредактировать шаблон элемента управления.
Основываясь на ответе 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, поскольку они позволяют выбирать строки там, где вы не обязательно этого хотите.
Просто по умолчанию ItemsPanel
не является VirtualizingStackPanel
, Вы должны изменить это:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>