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.

108
demandé sur Rachel 2010-05-06 23:29:03

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.

186
répondu DavidN 2017-11-17 08:27:38

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.

24
répondu Zodman 2016-10-27 15:11:35

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>
-3
répondu Abe Heidebrecht 2010-05-06 20:15:16