139 votes

Virtualiser un ItemsControl?

J'ai un ItemsControl contenant une liste de données que je souhaite virtualiser, toutefois 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 été en utilisant 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 l' ItemsControl d'un ListBox, je peux voir que l' Initialized événement s'exécute uniquement une poignée de fois (les énormes marges sont juste si je n'ai que de passer par quelques enregistrements), cependant, comme un ItemsControl chaque élément est initialisé.

J'ai essayé le réglage de l' ItemsControlPanelTemplate d'un VirtualizingStackPanel mais cela ne semble pas aider.

248voto

DavidN Points 2941

Il y a en fait beaucoup plus à lui que juste faire de l' ItemsPanelTemplate utilisation VirtualizingStackPanel. La valeur par défaut ControlTemplate pour ItemsControl n'ont pas d' ScrollViewer, ce qui est la clé de la virtualisation. L'ajout de la le modèle de contrôle par défaut pour ItemsControl (en utilisant le modèle de contrôle de ListBox comme modèle) nous donne les éléments suivants:

<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 grand outil de recherche par défaut sur les modèles de contrôle est de Me Montrer Le Modèle)

Choses à remarquer:

Vous devez définir ScrollViewer.CanContentScroll="True", voir ici pourquoi.

Notez également que j'ai mis VirtualizingStackPanel.VirtualizationMode="Recycling". Cela permettra de réduire le nombre de fois TextBlock_Initialized est appelé par le nombre de TextBlocks sont visibles sur l'écran. Vous pouvez en lire plus sur l'INTERFACE utilisateur de virtualisation ici .

EDIT: Oublié de le mentionner: une solution alternative, vous pouvez simplement remplacer ItemsControl avec ListBox:) Aussi, découvrez cette Optimisation de la Performance sur la page MSDN et avis qu' ItemsControl n'est pas dans les "Contrôles de mettre en Œuvre des Caractéristiques de Performance tableau", qui est pourquoi nous avons besoin de modifier le modèle de contrôle.

55voto

Zodman Points 329

En vous 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="ItemsControlVirtialisedStyle" 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 le souhaitez pas nécessairement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X