2010-05-06 9 views
103

Ho un ItemsControl contenente un elenco di dati che vorrei virtualizzare, tuttavia VirtualizingStackPanel.IsVirtualizing="True" non sembra funzionare con un ItemsControl.Virtualizzare un ItemsControl?

È proprio questo il caso o c'è un altro modo per farlo che non conosco?

Per testare Sono stato con il seguente blocco di codice:

<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> 

Se cambio la ItemsControl ad un ListBox, posso vedere che l'evento Initialized viene eseguito solo una manciata di volte (gli enormi margini sono solo così devo solo passare attraverso alcuni record), tuttavia come ItemsControl ogni elemento viene inizializzato.

Ho provato a impostare ItemsControlPanelTemplate su un VirtualizingStackPanel ma ciò non sembra essere di aiuto.

risposta

178

In realtà c'è molto di più ad esso non solo rendendo l'uso ItemsPanelTemplateVirtualizingStackPanel. Il valore predefinito ControlTemplate per ItemsControl non ha uno ScrollViewer, che è la chiave per la virtualizzazione. L'aggiunta al modello di controllo di default per ItemsControl (utilizzando il modello di controllo per ListBox come modello) ci dà la seguente:

<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 ottimo strumento per guardare modelli di controllo di default è Show Me The Template)

Cose da notare:

È necessario impostare ScrollViewer.CanContentScroll="True", vedere here per il motivo.

Si noti inoltre che ho inserito VirtualizingStackPanel.VirtualizationMode="Recycling". Ciò ridurrà il numero di volte in cui TextBlock_Initialized viene chiamato a molti TextBlocks visibili sullo schermo. Puoi leggere di più sulla virtualizzazione dell'interfaccia utente here . tavolo come una soluzione alternativa, si può semplicemente sostituire ItemsControl con ListBox :) Inoltre, controlla questo Optimizing Performance on MSDN page e notare che ItemsControl non è nei "controlli che implementano Caratteristiche prestazionali":

EDIT: Ho dimenticato di affermare l'ovvio , motivo per cui è necessario modificare il modello di controllo.

+1

Grazie, è esattamente il tipo di cosa che stavo cercando!Stavo cercando un diverso tipo di comportamento di selezione rispetto a una listbox e al tempo pensavo che sarebbe stato più semplice fare con un controllo degli oggetti. – Rachel

+0

ListView funziona anche per questo. –

+0

Se questo comando items è ulteriormente annidato, dovresti dargli un'altezza. Altrimenti lo scrollviewer non viene mostrato. – buckley

-3

È solo che il valore predefinito ItemsPanel non è un VirtualizingStackPanel. È necessario cambiare:

<ItemsControl> 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <VirtualizingStackPanel /> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
</ItemsControl> 
+0

Grazie, ma ho già provato questo e non ha funzionato. – Rachel

+6

Sono giù votandolo come la soluzione è incompleta. È necessario utilizzare un visualizzatore di scorrimento nel modello per abilitare la virtualizzazione. –

22

Sulla risposta di DavidN, ecco uno stile che si può utilizzare su un ItemsControl di virtualizzare è:

<!--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> 

Non mi piace il suggerimento di utilizzare un ListBox in quanto consentono la selezione di righe in cui si fa non necessariamente lo voglio

Problemi correlati