У меня есть календарный элемент управления со столбцом для каждого дня недели и семь ObservableCollections, которые могут содержать до ста или более элементов каждый.
Я хотел бы иметь возможность вертикально прокручивать их [Edit] одновременным образом [/ Edit] при их виртуализации.
Прямо сейчас у меня есть следующий макет
<ScrollViewer>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- Monday -->
<Border Grid.Column="0">
<ItemsControl ItemsSource="{Binding Monday}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" ... />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<MyControl Item="{Binding}" ... />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
...
</Grid>
</ScrollViewer>
Тем не менее, производительность сомнительна, и я думаю, что это связано с тем, что ScrollViewer позволяет Grid -> Border -> VirtualizingStackPanel вертикально расширяться, а не виртуализировать вообще. Это так?
(Sidenote: я попытался удалить ScrollViewer и добавить CanVerticallyScroll = «True» в VirtualizingStackPanels и ожидал, что они будут прокручиваться независимо, чтобы проверить, лучше ли производительность, но они вообще не будут прокручиваться)
Каким может быть правильный макет для этого?
Изменить : отображение 100 элементов для каждого столбца (всего 700) занимает 13 секунд, прокрутка на самом деле в порядке
[Edit2] : Из-за требуемой одновременной прокрутки я попытался создать новую коллекцию, содержащую «одну строку» (7 элементов) и это как шаблон в списке. Ужасные результаты [/ Edit2]
Всего 2 ответа
Чтобы сделать виртуализацию ItemsControl, вам нужно не только использовать VirtualizingStackPanel, но также добавить ScrollViewer в шаблон элемента ItemsControl (обычно у ItemsControl нет ScrollViewer).
Добавьте этот шаблон в свой элемент ItemsControl:
<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 на ListBox, у которого уже есть все необходимое для виртуализации вашего списка.
Решение проблемы состояло в том, чтобы не отображать семь столбцов каждый с одним виртуализированным элементом ItemsControl в ScrollViewer (который, казалось, расширил ItemsControls и заставил их рисовать все элементы управления), но визуализировал с помощью одного элемента ItemsControl, разрешив прокрутку через ItemsControl.Template и отображая один по очереди.
Я создал новую коллекцию и схватил объекты «за строку»,
for(int PI = 0; PI < MathHelper.Max(Monday.Count, Tuesday.Count, Wednesday.Count, Thursday.Count, Friday.Count, Saturday.Count, Sunday.Count); PI++)
{
Presentation.Add(new WeekRow(
Monday.Count > PI ? Monday[PI] : null,
Tuesday.Count > PI ? Tuesday[PI] : null,
Wednesday.Count > PI ? Wednesday[PI] : null,
Thursday.Count > PI ? Thursday[PI] : null,
Friday.Count > PI ? Friday[PI] : null,
Saturday.Count > PI ? Saturday[PI] : null,
Sunday.Count > PI ? Sunday[PI] : null
));
}
И отобразили их, используя это
<ItemsControl ItemsSource="{Binding Presentation}" VirtualizingPanel.ScrollUnit="Pixel">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True" Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel
Orientation="Vertical"
IsVirtualizing="True"
VirtualizationMode="Recycling">
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<MyControl Item="{Binding Monday}" Grid.Column="0" ... />
...
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate
</ItemsControl>
Что действительно важно здесь, это установить CanContentScroll-Свойство ScrollViewer в true или иначе виртуализация не будет работать (кто знает почему). Это отсутствовало в ответе @ Marc.
Edit: То, что также стоит упомянуть, заключается в том, что для получения «гладкой прокрутки» (ну, как «гладкая», как может получить wpf ..), нужно установить VirtualizingPanel.ScrollUnit в «Pixel». Но он работает только при установке в ItemsControl, а не в ScrollViewer, ItemsPresenter или VirtualizationStackPanel. Для любой причины.