La virtualizzazione di un ItemsControl?
-
03-10-2019 - |
Domanda
Ho un ItemsControl
contenente un elenco di dati che vorrei virtualizzare, tuttavia VirtualizingStackPanel.IsVirtualizing="True"
non sembra funzionare con un ItemsControl
.
E 'davvero questo il caso o c'è un altro modo di fare questo che io non sono a conoscenza?
Per testare Sono stato con 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 la ItemsControl
ad un ListBox
, posso vedere che l'evento Initialized
viene eseguito solo una manciata di volte (gli enormi margini sono così ho solo passare attraverso alcuni record), ma come un ItemsControl
ogni elemento diventa inizializzato.
Ho provato a fissare il ItemsControlPanelTemplate
ad un VirtualizingStackPanel
ma che non sembrano aiutare.
Soluzione
In realtà c'è molto di più ad esso non solo rendendo l'uso ItemsPanelTemplate
VirtualizingStackPanel
. Il ControlTemplate
predefinito per ItemsControl
non dispone di un ScrollViewer
, che è la chiave per la virtualizzazione. L'aggiunta al modello di controllo di default per ItemsControl
(utilizzando il modello di controllo per ListBox
come modello) ci dà la seguente:
<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>
(a proposito, un grande strumento per guardare modelli di controllo di default è Show Me The Template )
cose da notare:
Devi ScrollViewer.CanContentScroll="True"
set, vedi qui per il motivo.
Si noti inoltre che ho messo VirtualizingStackPanel.VirtualizationMode="Recycling"
. Ciò consentirà di ridurre il numero di volte TextBlock_Initialized
è chiamata a tuttavia molte TextBlocks sono visibili sullo schermo. Si può leggere di più sulla virtualizzazione UI qui
.
EDIT: Ho dimenticato di affermare l'ovvio: come una soluzione alternativa, si può semplicemente sostituire ItemsControl
con ListBox
:)
Inoltre, controllare questo Ottimizzare le prestazioni a pagina MSDN e notare che ItemsControl
non è nei "controlli che implementano delle prestazioni caratteristiche" da tavolo, che è il motivo per cui abbiamo bisogno di modificare il modello di controllo.
Altri suggerimenti
Sulla risposta di DavidN, ecco uno stile che si può utilizzare su un ItemsControl di virtualizzare è:
<!--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 consentono la selezione di righe in cui non necessariamente desidera.
E 'solo che il ItemsPanel
di default non è una VirtualizingStackPanel
. È necessario cambiare:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>