Virtualiser un ItemsControl?

125

J'ai un ItemsControlcontenant une liste de données que je voudrais virtualiser, mais VirtualizingStackPanel.IsVirtualizing="True"ne semble pas fonctionner avec un fichier ItemsControl.

Est-ce vraiment le cas ou y a-t-il une autre façon de le faire dont 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 ItemsControlen a ListBox, je peux voir que l' Initializedévénement ne s'exécute qu'une poignée de fois (les marges énormes sont juste pour que je n'ai qu'à parcourir quelques enregistrements), mais ItemsControlchaque élément est initialisé.

J'ai essayé de régler le ItemsControlPanelTemplatesur a VirtualizingStackPanelmais cela ne semble pas aider.

Rachel
la source

Réponses:

219

Il y a en fait beaucoup plus que la simple ItemsPanelTemplateutilisation VirtualizingStackPanel. La valeur ControlTemplatepar défaut de ItemsControln'a pas de ScrollViewer, qui est la clé de la virtualisation. L'ajout au modèle de contrôle par défaut pour ItemsControl(en utilisant le modèle de contrôle pour ListBoxcomme modèle) nous donne ce qui suit:

<ItemsControl ItemsSource="{Binding AccountViews.Tables[0]}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <TextBlock Initialized="TextBlock_Initialized"
                 Text="{Binding Name}" />
    </DataTemplate>
  </ItemsControl.ItemTemplate>

  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel IsVirtualizing="True"
                              VirtualizationMode="Recycling" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>

  <ItemsControl.Template>
    <ControlTemplate TargetType="ItemsControl">
      <Border BorderThickness="{TemplateBinding BorderThickness}"
              BorderBrush="{TemplateBinding BorderBrush}"
              Background="{TemplateBinding Background}">
        <ScrollViewer CanContentScroll="True" 
                      Padding="{TemplateBinding Padding}"
                      Focusable="False">
          <ItemsPresenter />
        </ScrollViewer>
      </Border>
    </ControlTemplate>
  </ItemsControl.Template>
</ItemsControl>

(BTW, un excellent outil pour regarder les modèles de contrôle par défaut est Show Me The Template )

Choses à remarquer:

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

Notez également que je mets VirtualizingStackPanel.VirtualizationMode="Recycling". Cela réduira le nombre de fois où TextBlock_Initializedest appelé quel que soit le nombre de TextBlocks visibles à l'écran. Vous pouvez en savoir plus sur la virtualisation de l'interface utilisateur ici .

EDIT: J'ai oublié de dire l'évidence: comme solution alternative, vous pouvez simplement remplacer ItemsControlpar ListBox:) Aussi, consultez cette page d' optimisation des performances sur MSDN et notez qu'elle ItemsControlne figure pas dans le tableau "Contrôles qui implémentent les fonctionnalités de performance", c'est pourquoi nous devons éditer le modèle de contrôle.

DavidN
la source
1
Merci, c'est exactement le genre de chose que je recherchais! Je cherchais un type de comportement de sélection différent de celui d'une listbox et à l'époque je pensais que ce serait plus facile à faire avec un contrôle d'éléments.
Rachel
Si ce contrôle des éléments est davantage imbriqué, vous devez également lui donner une hauteur. Sinon, le scrollviewer n'est pas affiché.
buckley
9
"Notez également que j'ai mis VirtualizingStackPanel.VirtualizationMode = Recycling". N'est-il pas censé faire partie de l'échantillon que vous fournissez?
buckley
Est -ce que la virtualisation a également le travail lorsque wrap ItemsControlen ScrollViewerinstread ajoutant Scrollà ControlTemplate?
démo du
@DavidN Où et comment puis-je mettre les en-têtes de colonne dans votre solution?
Ozkan
37

En vous basant 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 là où vous ne le souhaitez pas nécessairement.

Zodman
la source
-3

C'est juste que la valeur par défaut ItemsPaneln'est pas un VirtualizingStackPanel. Vous devez le changer:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Abe Heidebrecht
la source
8
Je suis contre le vote car la solution est incomplète. Vous devez utiliser un scrollviewer dans le modèle pour activer la virtualisation.
Saraf Talukder