Virtualizzare un ItemsControl?


125

Ho un ItemsControlcontenente un elenco di dati che vorrei virtualizzare, tuttavia VirtualizingStackPanel.IsVirtualizing="True"non sembra funzionare con un file ItemsControl.

È davvero così o c'è un altro modo di farlo di cui non sono a conoscenza?

Per testare ho utilizzato il seguente blocco di codice:

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

Se cambio il ItemsControlin a ListBox, posso vedere che l' Initializedevento viene eseguito solo una manciata di volte (gli enormi margini sono solo così devo solo passare attraverso alcuni record), tuttavia ItemsContrologni elemento viene inizializzato.

Ho provato a impostare il ItemsControlPanelTemplatesu a VirtualizingStackPanelma questo non sembra aiutare.

Risposte:


219

In realtà c'è molto di più oltre al semplice ItemsPanelTemplateutilizzo VirtualizingStackPanel. L'impostazione predefinita ControlTemplateper ItemsControlnon ha un ScrollViewer, che è la chiave per la virtualizzazione. L'aggiunta al modello di controllo predefinito per ItemsControl(utilizzando il modello di controllo per ListBoxcome modello) fornisce quanto segue:

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

(A proposito, un ottimo strumento per guardare i modelli di controllo predefiniti è Show Me The Template )

Cose da notare:

Devi impostare ScrollViewer.CanContentScroll="True", vedi qui per il motivo.

Notare anche che ho messo VirtualizingStackPanel.VirtualizationMode="Recycling". Ciò ridurrà il numero di volte in cui TextBlock_Initializedviene chiamato a tutti i blocchi di testo visibili sullo schermo. Puoi leggere di più sulla virtualizzazione dell'interfaccia utente qui .

MODIFICA: Ho dimenticato di affermare l'ovvio: come soluzione alternativa, puoi semplicemente sostituire ItemsControlcon ListBox:) Inoltre, controlla questa pagina di ottimizzazione delle prestazioni su MSDN e nota che ItemsControlnon è nella tabella "Controlli che implementano le funzionalità di prestazione", motivo per cui dobbiamo modificare il modello di controllo.


1
Grazie, è esattamente il tipo di cosa che stavo cercando! Stavo cercando un diverso tipo di comportamento di selezione rispetto a una casella di riepilogo e al momento ho pensato che sarebbe stato più semplice da fare con un controllo degli elementi.
Rachel

Se questo controllo degli elementi è ulteriormente annidato, dovresti anche dargli un'altezza. Altrimenti lo scrollviewer non viene mostrato.
Buckley

9
"Notare anche che ho messo VirtualizingStackPanel.VirtualizationMode = Recycling". Non dovrebbe essere nel campione fornito?
Buckley

Fa virtualizzazione anche il lavoro quando l'involucro ItemsControlin ScrollViewerinstread aggiunta Scrolla ControlTemplate?
demo

@DavidN Dove o come posso inserire le intestazioni di colonna nella tua soluzione?
Ozkan

37

Basandosi sulla risposta di DavidN, ecco uno stile che puoi utilizzare su un ItemsControl per virtualizzarlo:

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

Non mi piace il suggerimento di utilizzare un ListBox in quanto consente la selezione di righe in cui non lo si desidera necessariamente.


-3

È solo che l'impostazione predefinita ItemsPanelnon è un file VirtualizingStackPanel. Devi cambiarlo:

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

8
Non voterò perché la soluzione è incompleta. È necessario utilizzare un visualizzatore di scorrimento nel modello per abilitare la virtualizzazione.
Saraf Talukder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.