Memvirtualisasikan ItemsControl?

125

Saya memiliki ItemsControldaftar berisi data yang ingin saya virtualisasikan, namun VirtualizingStackPanel.IsVirtualizing="True"tampaknya tidak berfungsi dengan file ItemsControl.

Apakah benar demikian atau adakah cara lain untuk melakukan ini yang tidak saya sadari?

Untuk menguji saya telah menggunakan blok kode berikut:

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

Jika saya mengubah ItemsControlke a ListBox, saya dapat melihat bahwa Initializedacara hanya berjalan beberapa kali (margin besar hanya jadi saya hanya perlu melalui beberapa catatan), namun ItemsControlsetiap item diinisialisasi.

Saya telah mencoba menyetel ItemsControlPanelTemplateke a VirtualizingStackPaneltetapi tampaknya tidak membantu.

Rachel
sumber

Jawaban:

219

Sebenarnya ada lebih dari sekedar ItemsPanelTemplatememanfaatkan VirtualizingStackPanel. Default ControlTemplateuntuk ItemsControltidak memiliki ScrollViewer, yang merupakan kunci virtualisasi. Menambahkan ke template kontrol default untuk ItemsControl(menggunakan template kontrol untuk ListBoxsebagai template) memberi kita hal berikut:

<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, alat hebat untuk melihat templat kontrol default adalah Tunjukkan Templatnya )

Hal yang perlu diperhatikan:

Anda harus mengatur ScrollViewer.CanContentScroll="True", lihat di sini untuk alasannya.

Juga perhatikan bahwa saya meletakkan VirtualizingStackPanel.VirtualizationMode="Recycling". Ini akan mengurangi jumlah panggilan TextBlock_Initializedke berapa pun TextBlock yang terlihat di layar. Anda dapat membaca lebih lanjut tentang virtualisasi UI di sini .

EDIT: Lupa menyatakan yang sudah jelas: sebagai solusi alternatif, Anda dapat mengganti ItemsControldengan ListBox:) Juga, lihat halaman Mengoptimalkan Kinerja di MSDN ini dan perhatikan yang ItemsControltidak ada di tabel "Kontrol yang Menerapkan Fitur Kinerja", itulah sebabnya kita perlu mengedit template kontrol.

DavidN
sumber
1
Terima kasih, itulah hal yang saya cari! Saya sedang mencari jenis perilaku pemilihan yang berbeda dari kotak daftar dan pada saat itu saya pikir itu akan paling mudah dilakukan dengan kontrol item.
Rachel
Jika itemscontrol ini selanjutnya disarangkan, Anda juga harus memberikan ketinggian. Jika tidak, scrollviewer tidak akan ditampilkan.
buckley
9
"Juga perhatikan bahwa saya meletakkan VirtualizingStackPanel.VirtualizationMode = Recycling". Bukankah itu seharusnya ada dalam sampel yang Anda berikan?
Buckley
Apakah virtualisasi juga bekerja ketika bungkus ItemsControlke ScrollViewerinstread menambahkan Scrollke ControlTemplate?
demo
@DavidN Di mana atau bagaimana saya dapat meletakkan tajuk kolom dalam solusi Anda?
Ozkan
37

Berdasarkan jawaban DavidN, berikut adalah gaya yang dapat Anda gunakan pada ItemsControl untuk memvirtualisasikannya:

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

Saya tidak suka saran untuk menggunakan ListBox karena memungkinkan pemilihan baris di mana Anda tidak perlu menginginkannya.

Zodman
sumber
-3

Hanya saja defaultnya ItemsPanelbukan VirtualizingStackPanel. Anda perlu mengubahnya:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Abe Heidebrecht
sumber
8
Saya tidak memberikan suara karena solusinya tidak lengkap. Anda perlu menggunakan scrollviewer di template untuk mengaktifkan virtualisasi.
Saraf Talukder