La virtualización de una ItemsControl?
-
03-10-2019 - |
Pregunta
Tengo una ItemsControl
que contiene una lista de datos que me gustaría para virtualizar, sin embargo VirtualizingStackPanel.IsVirtualizing="True"
no parecen funcionar con un ItemsControl
.
¿Es esto realmente así o hay otra manera de hacer esto que no soy consciente de?
Para probar He estado usando el siguiente bloque de código:
<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>
Si cambio la ItemsControl
a un ListBox
, puedo ver que el evento Initialized
sólo se ejecuta un puñado de veces (los enormes márgenes son tan sólo tengo que pasar por unos pocos registros), sin embargo, como un ItemsControl
cada artículo se inicializado.
He intentado fijar la ItemsControlPanelTemplate
a un VirtualizingStackPanel
pero eso no parece ayudar.
Solución
En realidad hay mucho más a él que sólo hacer uso del ItemsPanelTemplate
VirtualizingStackPanel
. El ControlTemplate
defecto para ItemsControl
no tiene un ScrollViewer
, que es la clave para la virtualización. Adición a la plantilla el control predeterminado para ItemsControl
(usando la plantilla de control para ListBox
como una plantilla), nos da la siguiente:
<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>
(Por cierto, una gran herramienta para analizar las plantillas de control por defecto es me muestra la plantilla )
cosas a destacar:
Usted tiene que fijar ScrollViewer.CanContentScroll="True"
, consulte aquí por qué.
También aviso que puso VirtualizingStackPanel.VirtualizationMode="Recycling"
. Esto reducirá el número de veces TextBlock_Initialized
se llama a TextBlocks embargo muchos son visibles en la pantalla. Usted puede leer más en la interfaz de usuario de virtualización aquí
.
EDIT: Se olvidó de decir lo obvio: como una solución alternativa, sólo puede sustituir con ItemsControl
ListBox
:)
Además, echa un vistazo a este Optimización del rendimiento en la página de MSDN y el aviso de que ItemsControl
no está en los "controles que implementan características de rendimiento" de mesa, por lo que tenemos que editar la plantilla de control.
Otros consejos
Sobre la base de la respuesta de DavidN, aquí es un estilo que puede utilizar en una ItemsControl para virtualizar él:
<!--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>
No me gusta la sugerencia de utilizar un cuadro de lista, ya que permiten la selección de filas en las que no necesariamente lo desee.
Es sólo que el ItemsPanel
por defecto no es un VirtualizingStackPanel
. Es necesario cambiarlo:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>