Виртуализация

Последнее обновление: 26.02.2016

Данные, которыми оперирует программа, могут насчитывать сотни и даже тысячи объектов. Возможно, нам потребуется все эти объекты выводить в списочные элементы - ListBox, ListView, DataGrid. Однако если мы разом загрузим все тысячи объектов в эти элементы управления, то можем столкнуться с проблемой падения производительности. И в этом случае нам надо будет воспользоваться механизмом виртуализации.

Виртуализация позволяет создавать элементу управления контейнер только для непосредственно отображаемых объектов списка. Только для них выделяется память, при этом элемент хранит схему структуры данных, чтобы при прокрутке или изменении видимых объектов соответственно изменить содержимое контейнера. То есть, если в ListView загружено 10000 объектов, однако в реальности элемент отображает только 10 объектов, то ListView создает только 10 объектов ListViewItem. Остальные объекты существуют для ListView только потенциально, в реальности они начинают использоваться только тогда, когда попадают в область видимости по мере прокрутки. В итоге приложение использует меньше памяти, работает быстрее, что повышает производительность.

По умолчанию виртуализация включена для элементов ListView, ListBox и DataGrid, когда они используют привязку к данным в коллекциях.

Для элемента TreeView виртуализацию можно включить, присвоив вложенному свойству VirtualizingStackPanel.IsVirtualizing значение true:

<TreeView VirtualizingStackPanel.IsVirtualizing="True" ... >

Если же надо использовать виртуализацию для каких-то своих элементов, производных от ItemsControl, или для уже существующих элементов управления, которые используют StackPanel (например, ComboBox), то в этих случаях надо установить свойство ItemsPanel для класса VirtualizingStackPanel и присвоить свойству IsVirtualizing значение true. Например:

<ComboBox VirtualizingStackPanel.IsVirtualizing="True">
	<ComboBox.ItemsPanel>
		<ItemsPanelTemplate>
			<VirtualizingStackPanel />
		</ItemsPanelTemplate>
	</ComboBox.ItemsPanel>
</ComboBox>

Однако при наличии некоторых условий виртуализация отключается:

  • если контейнеры элементов добавляются напрямую к элементу управления ItemsControl. Например, если объекты ListBoxItem добавляются к ListBox, то ListBox не будет виртуализировать эти ListBoxItem

  • если объект ItemsControl содержит контейнеры элементов различных типов. Например, объект Menu, может содержать объекты типа Separator и MenuItem

  • если для прикрепленного свойства VirtualizingStackPanel.IsVirtualizing установлено значение false

  • если для прикрепленного свойства ScrollViewer.CanContentScroll установлено значение false

Повторное использование контейнера

При заполнении объекта ItemsControl создается контейнер элементов для каждого отображаемого при прокрутке элемента. Когда элемент в результате прокрутки перестает отображаться, его контейнер удаляется. Повторное использование контейнера позволяет элементу управления повторно использовать существующие контейнеры элементов для различных элементов данных, чтобы контейнеры элементов не создавались и не удалялись постоянно при прокрутке. Для этого надо использовать свойство VirtualizingStackPanel.VirtualizationMode="Recycling":

<ListBox Height="150" ItemsSource="{StaticResource data}" 
             VirtualizingStackPanel.VirtualizationMode="Recycling" />

По умолчанию данное свойство отключено для все элементов управления списками за исключением элемента DataGrid. Подобная техника снижает потребление памяти и необходимость утилизовать ненужные объекты сборщиком мусора каждый раз, когда они станут не нужны.

Кэширование

Для большей оптимизации производительности VirtualizingStackPanel производит кэширование объектов. С помощью свойств CacheLength и CacheLengthUnit класса VirtualizingStackPanel мы можем настроить кэширование видимых объектов в списках.

CacheLengthUnit позволяет указать тип кэша - это может быть отдельные объекты списка, страницы списка (страницы представляет набор объектов, одновременно отображаемых в списке) и пиксели (используется преимущественно для изображений).

Свойство CacheLength устанавливает количество единиц (объектов, страниц или пикселей), которые будут кэшироваться вирутальной панелью.

Например, по умолчанию используются следующие настройки:

<ListBox VirtualizingStackPanel.CacheLength="1" VirtualizingStackPanel.CacheLengthUnit="Page" ... />

То есть VirtualizingStackPanel будет кэшировать одну страницу объектов до и одну страницу после тех, которые отображаются в ListBox. В итоге при прокрутке вверх или вниз все новые элементы будут появляться плавно и без задержек, так как они берутся из кэша VirtualizingStackPanel. Но мы можем изменить стандартные настройки, например, следующим образом:

<ListBox VirtualizingStackPanel.CacheLength="30" VirtualizingStackPanel.CacheLengthUnit="Item" ... />

В данном случае будет кэшировать 30 объектов до и после тех, которые отображаются в ListBox.

Также можно отдельно настроить кэширование тех объектов, которые находятся до отображаемых, и тех объектов, которые после:

<ListBox VirtualizingStackPanel.CacheLength="30, 50" VirtualizingStackPanel.CacheLengthUnit="Item" ... />

В данном случае будет кэшировать 30 объектов до тех, которые отображаются, и 50 объектов, которые в списке после тех, которые отображаются в ListBox.

Отложенная прокрутка

Отложенная прокрутка (deffered scrolling) представляет технику оптимизации, при которой обновление данных в окне элемента управления происходит только тогда, когда пользователь отпускает скрол. Чтобы реализовать отложенную прокрутку, надо установить для свойства IsDeferredScrollingEnabled значение true:

<ListBox ScrollViewer.IsDeferredScrollingEnabled="True" ... />

IsDeferredScrollingEnabled является вложенным свойством и может быть задано в объекте ScrollViewer и в любом элементе управления с ScrollViewer в его шаблоне элементов управления.

По умолчанию прокрутка в элементах-списках осуществляет по объектам: при перемещении скрола мы перемещаемся на один объект в списке вниз или вверх. Однако мы можем использовать более плавную прокрутку, установив для свойства VirtualizingStackPanel.ScrollUnit значение Pixel:

<ListBox VirtualizingStackPanel.ScrollUnit="Pixel" ... />
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850