Проблемы производительности WPF ComboBox путем привязки больших коллекций
Я пытаюсь связать большую коллекцию с ComboBox, и я столкнулся с проблемами производительности при открытии всплывающего окна ComboBox. Я искал интернет и обнаружил, что использование VirtualizationStackPanel в качестве шаблона панели элементов может помочь, но это помогло лишь частично. Если я привяжу большую коллекцию к ComboBox, я могу быстро открыть всплывающее окно, это нормально, но если после этого я привяжу другую коллекцию к ComboBox и снова попытаюсь открыть всплывающее окно, она станет очень медленной. То же самое происходит, если вы открываете всплывающее окно для пустого ComboBox, затем связываете большую коллекцию и снова пытаетесь открыть всплывающее окно - для всплывающего окна требуется несколько секунд.
Вот XAML:
<ComboBox Name="cbBlah">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
и пример кода для связывания для воспроизведения проблемы:
var list = new List<string>();
for (var i = 0; i < new Random().Next(9000, 10000); i++)
list.Add(i.ToString());
cbBlah.ItemsSource = list;
Я попытался сделать панель виртуализации стека, чтобы она выглядела следующим образом:
<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />
но это не помогает, кажется, что VirtualizationMode игнорируется, поэтому всплывающее окно открывается очень быстро только в первый раз, а затем, каждый раз после привязки изменений, оно очень медленно.
UPDATE. Я думал о том, что не привязываю новую коллекцию каждый раз, но привязываю ObservableCollection один раз, а затем просто меняю ее содержимое. То же самое, как только содержимое коллекции меняется, открытие всплывающего окна по-прежнему занимает несколько секунд: (
Ответы
Ответ 1
Согласно этому блогу: http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx
Я тестировал его с помощью этого кода:
<ComboBox Name="cbBlah" ItemsSource="{Binding}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
Он отлично работает в первый раз и в следующий раз. Не нужно кодировать эти строки:
<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />
Надеюсь, это поможет вам.
Ответ 2
У меня была проблема с низкой производительностью. Но я создал класс, унаследовавший форму Combobox, поэтому я хотел бы сделать это программно. Итак, вот это решение для других гуглеров.
ItemsPanel = new ItemsPanelTemplate();
var stackPanelTemplate = new FrameworkElementFactory(typeof (VirtualizingStackPanel));
ItemsPanel.VisualTree = stackPanelTemplate;
Ответ 3
Я просто столкнулся с этой проблемой. Я использую этот код в пользовательском поле со списком с шаблоном стиля. Когда я запускал свой код в режиме отладки VS, виртуализация не работала должным образом. Как только я запустил его вне отладки, я могу переключить содержимое ObservableCollection без блокировки UI. Это также может помочь, если вы установите максимальную высоту и максимальную ширину.
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
<Popup>
<Border/>
<ScrollViewer>
<VirtualizingStackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Grid>
</Popup>