2010-05-06 10 views
103

Mam ItemsControl zawierający listę danych, które chciałbym wirtualizacji, jednak VirtualizingStackPanel.IsVirtualizing="True" nie wydaje się pracować z ItemsControl.Wirtualizacja elementu ItemsControl?

Czy tak jest naprawdę, czy jest inny sposób robienia tego, czego nie jestem świadomy?

Aby przetestować Używam następujący fragment kodu:

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

Jeśli zmienię ItemsControl do ListBox, widzę, że zdarzenie Initialized działa tylko kilka razy (ogromne marże są tylko dlatego, że muszę przejść przez kilka rekordów), jednak jako ItemsControl każdy element zostanie zainicjowany.

Próbowałem ustawić ItemsControlPanelTemplate na VirtualizingStackPanel, ale to nie wydaje się pomóc.

Odpowiedz

178

Jest rzeczywiście znacznie więcej, niż po prostu dokonywania ItemsPanelTemplate korzystanie VirtualizingStackPanel. Domyślna ControlTemplate dla ItemsControl nie ma ScrollViewer, która jest kluczem do wirtualizacji. Dodanie do szablonu kontroli domyślną ItemsControl (przy użyciu szablonu kontroli dla ListBox jako szablonu) daje nam następujące:

<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, to doskonałe narzędzie dla patrząc na szablonach kontroli domyślnych jest Show Me The Template)

Rzeczy do zauważenia:

Musisz ustawić ScrollViewer.CanContentScroll="True", patrz here dlaczego.

Zauważ również, że wstawiłem VirtualizingStackPanel.VirtualizationMode="Recycling". Spowoduje to zmniejszenie liczby razy, gdy TextBlock_Initialized zostanie wywołany, ale na ekranie pojawi się wiele blokad TextBlock. Więcej informacji na temat wirtualizacji UI można uzyskać pod adresem here .

EDIT: Zapomniałem stwierdzić oczywisty: jako alternatywnego rozwiązania, można po prostu zastąpić ItemsControl z ListBox :) również sprawdzić ten Optimizing Performance on MSDN page i zauważ, że ItemsControl nie jest w „kontroli, które wdrażają cech eksploatacyjnych” stół , dlatego musimy edytować szablon kontrolny.

+1

Dziękuję, to jest dokładnie to, czego szukałem!Szukałem innego sposobu selekcji niż listbox i wtedy wydawało mi się, że najłatwiej będzie zrobić z kontrolką przedmiotów. – Rachel

+0

ListView działa również w tym przypadku. –

+0

Jeśli ta kontrola przedmiotów jest dodatkowo zagnieżdżona, należy również podać jej wysokość. W przeciwnym razie przewijarka nie jest wyświetlana. – buckley

-3

Po prostu domyślny ItemsPanel nie jest VirtualizingStackPanel. Trzeba to zmienić:

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

Dzięki, ale już próbowałem i to nie zadziałało. – Rachel

+6

Głosuję w dół, ponieważ rozwiązanie jest niekompletne. Aby włączyć wirtualizację, musisz użyć scrollviewera w szablonie. –

22

Opierając się na odpowiedź DavidN, oto jest styl można użyć na ItemsControl aby go wirtualizacji:

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

Nie lubię sugestię używać ListBox jak pozwalają one na wybór wierszy, gdzie można zrobić niekoniecznie tego chcę.

Powiązane problemy