Віртуалізація елементів управління?


125

У мене є ItemsControlсписок даних, які я б хотів віртуалізувати, однак VirtualizingStackPanel.IsVirtualizing="True", схоже , це не працює з ItemsControl.

Це справді так чи є інший спосіб зробити це, про який я не знаю?

Для тестування я використовував наступний блок коду:

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

Якщо я перейду ItemsControlна "a" ListBox, я можу побачити, що Initializedподія працює лише кілька разів (величезні поля так само, що мені потрібно пройти лише кілька записів), однак, як ItemsControlкожен елемент стає ініціалізованим.

Я спробував встановити значення " ItemsControlPanelTemplateа", VirtualizingStackPanelале це, здається, не допоможе.

Відповіді:


219

Насправді це набагато більше, ніж просто ItemsPanelTemplateвикористовувати VirtualizingStackPanel. Значення за замовчуванням ControlTemplateдля ItemsControlне має ScrollViewer, що є ключем до віртуалізації. Додавання до шаблону керування за замовчуванням для ItemsControl(використовуючи шаблон управління ListBoxяк шаблон) дає нам наступне:

<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 - чудовий інструмент для перегляду шаблонів керування за замовчуванням - Show Me The Template )

Що потрібно помітити:

Ви повинні встановити ScrollViewer.CanContentScroll="True", дивіться тут, чому.

Також зауважте, що я кладу VirtualizingStackPanel.VirtualizationMode="Recycling". Це зменшить TextBlock_Initializedкількість викликів разів , однак на екрані видно багато TextBlocks. Більше про віртуалізацію інтерфейсу ви можете прочитати тут .

EDIT: Забув констатувати очевидне: як альтернативне рішення ви можете просто замінити ItemsControlна ListBox:) Крім того, ознайомтеся з цією оптимізацією продуктивності на сторінці MSDN і помітьте, що ItemsControlвона відсутня в таблиці "Контрольні функції, що реалізують продуктивність", саме тому нам потрібно відредагувати шаблон управління.


1
Дякую, це саме те, що я шукав! Я шукав іншого вибору поведінки вибору, ніж список списків, і тоді я думав, що це найпростіше зробити з контролем елементів.
Рейчел

Якщо цей елемент управління ще вкладений, вам слід також надати висоту. В іншому випадку переглядач прокрутки не відображається.
Баклі

9
"Також зауважте, що я поставив VirtualizingStackPanel.VirtualizationMode = Переробка". Хіба це не повинно бути у зразку, який ви надаєте?
Баклі

Є чи віртуалізація працювати , коли обгортка ItemsControlв ScrollViewerinstread додавання Scrollдо ControlTemplate?
демонстрація

@DavidN Де або як можна помістити заголовки стовпців у ваше рішення?
Озкан

37

Спираючись на відповідь DavidN, ось стиль, який ви можете використовувати на ItemControl для віртуалізації:

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

Мені не подобається пропозиція використовувати ListBox, оскільки вони дозволяють виділити рядки там, де ви цього не обов'язково бажаєте.


-3

Просто за замовчуванням ItemsPanelце не a VirtualizingStackPanel. Потрібно змінити:

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

8
Я голосую за те, що рішення є неповним. Щоб увімкнути віртуалізацію, в шаблоні потрібно використовувати прокручувач.
Сараф Талукдер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.