¿Virtualizando un control de ítems?


125

Tengo un que ItemsControlcontiene una lista de datos que me gustaría virtualizar, sin embargo VirtualizingStackPanel.IsVirtualizing="True", no parece funcionar con un ItemsControl.

¿Es este realmente el caso o hay otra forma de hacerlo que no conozco?

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 el ItemsControla a ListBox, puedo ver que el Initializedevento solo se ejecuta un puñado de veces (los enormes márgenes son solo para que solo tenga que revisar algunos registros), sin embargo, a medida ItemsControlque se inicializa cada elemento.

He intentado configurar el ItemsControlPanelTemplatea VirtualizingStackPanelpero eso no parece ayudar.

Respuestas:


219

En realidad, hay mucho más que solo hacer el ItemsPanelTemplateuso VirtualizingStackPanel. El valor predeterminado ControlTemplatepara ItemsControlno tiene un ScrollViewer, que es la clave para la virtualización. Agregar a la plantilla de control predeterminada para ItemsControl(usando la plantilla de control ListBoxcomo plantilla) nos da lo siguiente:

<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>

(Por cierto, una gran herramienta para mirar las plantillas de control predeterminadas es Show Me The Template )

Cosas a tener en cuenta:

Tienes que configurar ScrollViewer.CanContentScroll="True", mira aquí por qué.

También note que lo pongo VirtualizingStackPanel.VirtualizationMode="Recycling". Esto reducirá la cantidad de veces que TextBlock_Initializedse llama, sin embargo, muchos TextBlocks son visibles en la pantalla. Puede leer más sobre la virtualización de la interfaz de usuario aquí .

EDITAR: Olvidé decir lo obvio: como una solución alternativa, solo puede reemplazar ItemsControlcon ListBox:) Además, consulte esta página Optimización del rendimiento en MSDN y observe que ItemsControlno está en la tabla "Controles que implementan características de rendimiento", por eso Necesitamos editar la plantilla de control.


1
¡Gracias, ese es exactamente el tipo de cosas que estaba buscando! Estaba buscando un tipo diferente de comportamiento de selección que un cuadro de lista y en ese momento pensé que sería más fácil hacerlo con un control de elementos.
— Rachel,

Si este control de elementos está anidado aún más, también debe darle una altura. De lo contrario, no se muestra el scrollviewer.
— buckley

9
"Observe también que puse VirtualizingStackPanel.VirtualizationMode = Recycling". ¿No se supone que debe estar en la muestra que proporciona?
— Buckley

También lo hace la virtualización trabajo cuando envoltura ItemsControlen ScrollViewerinstread añadiendo Scrolla ControlTemplate?
— demo

@DavidN ¿Dónde o cómo puedo poner los encabezados de columna en su solución?
— Ozkan

37

Basándose en la respuesta de DavidN, aquí hay un estilo que puede usar en un ItemsControl para virtualizarlo:

<!--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 usar un ListBox ya que permiten la selección de filas donde no necesariamente lo quieres.


-3

Es solo que el valor predeterminado ItemsPanelno es a VirtualizingStackPanel. Necesitas cambiarlo:

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

8
Estoy votando porque la solución está incompleta. Debe usar un visor de desplazamiento en la plantilla para habilitar la virtualización.
— Saraf Talukder
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.