Virtualiser un ItemsControl?
j'ai un ItemsControl
contenant une liste de données que je souhaite virtualiser, mais VirtualizingStackPanel.IsVirtualizing="True"
ne semble pas fonctionner avec un ItemsControl
.
Est-ce vraiment le cas ou est-il une autre façon de faire ce que je ne suis pas au courant?
pour tester j'ai utilisé le bloc de code suivant:
<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 je change le ItemsControl
en ListBox
, je peux voir que l'événement Initialized
fonctionne seulement une poignée de fois (les marges énormes sont juste si je dois passer en revue quelques disques), cependant comme un ItemsControl
chaque élément est initialisé.
j'ai essayé le ItemsControlPanelTemplate
à un VirtualizingStackPanel
mais cela ne semble pas aider.
3 réponses
il y a en fait beaucoup plus à cela que de simplement faire le ItemsPanelTemplate
utiliser VirtualizingStackPanel
. La valeur par défaut ControlTemplate
pour ItemsControl
n'a pas de ScrollViewer
, qui est la clé de la virtualisation. En ajoutant au modèle de contrôle par défaut pour ItemsControl
(en utilisant le modèle de contrôle pour ListBox
comme modèle) nous obtenons ce qui suit:
<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>
(BTW, un excellent outil pour regarder les modèles de contrôle par défaut est me montrer le Modèle )
choses à remarquer:
vous devez mettre ScrollViewer.CanContentScroll="True"
, voir ici pour savoir pourquoi.
aussi noter que j'ai mis VirtualizingStackPanel.VirtualizationMode="Recycling"
. Cela permettra de réduire le nombre de fois où TextBlock_Initialized
est appelé, mais de nombreux textes sont visibles à l'écran. Vous pouvez en savoir plus sur la virtualisation de L'UI ici
.
EDIT: oublié d'énoncer l'évidence: comme solution alternative, vous pouvez simplement remplacer ItemsControl
par ListBox
:)
De plus, consultez ce Optimizing Performance sur la page MSDN et notez que ItemsControl
n'est pas dans la table "Controls That Implement Performance Features", c'est pourquoi nous devons éditer le modèle de contrôle.
en S'appuyant sur la réponse de DavidN, voici un style que vous pouvez utiliser sur un ItemsControl pour le virtualiser:
<!--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>
Je n'aime pas la suggestion d'utiliser un ListBox car ils permettent la sélection de lignes où vous ne voulez pas nécessairement.
c'est juste que la valeur par défaut ItemsPanel
n'est pas une VirtualizingStackPanel
. Vous devez le changer:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>