Question

J'ai un ItemsControl contenant une liste de données que je voudrais 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'utilise 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 à un ListBox, je peux voir que l'événement Initialized ne fonctionne que quelques fois (les énormes marges sont juste pour que je ne dispose que de passer par quelques dossiers), mais comme un ItemsControl chaque élément obtient initialisé.

Je l'ai essayé de placer le ItemsControlPanelTemplate à un VirtualizingStackPanel mais cela ne semble pas aider.

Était-ce utile?

La solution

Il est en réalité beaucoup plus à lui que juste faire l'utilisation de ItemsPanelTemplate VirtualizingStackPanel. Le ControlTemplate par défaut pour ItemsControl ne dispose pas d'un ScrollViewer, qui est la clé de la virtualisation. Ajout au modèle de contrôle par défaut pour ItemsControl (en utilisant le modèle de commande pour ListBox comme modèle) nous donne 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 Show Me Le modèle )

A noter:

Vous devez ScrollViewer.CanContentScroll="True" ensemble, voir pourquoi.

Notez également que je mets VirtualizingStackPanel.VirtualizationMode="Recycling". Cela permettra de réduire le nombre de fois TextBlock_Initialized est appelé à cependant beaucoup de TextBlocks sont visibles à l'écran. Vous pouvez en savoir plus sur l'interface utilisateur virtualisation ici .

EDIT: Vous avez oublié de dire l'évidence: comme solution de rechange, vous pouvez simplement remplacer ItemsControl avec ListBox :) En outre, consultez cette Optimisation des performances à la page MSDN et avis que ItemsControl n'est pas dans les « contrôles qui implémentent des caractéristiques de performances » table, ce qui est la raison pour laquelle nous devons modifier le modèle de contrôle.

Autres conseils

Miser sur la réponse de DavidN, voici un style que vous pouvez utiliser sur un ItemsControl pour virtualiser il:

<!--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 ne aime pas la suggestion d'utiliser un ListBox car ils permettent la sélection de lignes où vous ne voulez pas nécessairement.

Il est juste que la ItemsPanel par défaut n'est pas un VirtualizingStackPanel. Vous devez changer:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top