61

I'm trying to bind a large collection to a ComboBox and I faced performance problems when opening ComboBox's popup. I searched internet and found that using VirtualizingStackPanel as a items panel template might help, but it helped only partially. If I bind a large collection to a ComboBox, I could open popup very quickly, that's ok, but if after that I bind another collection to a ComboBox and try to open popup again, it becomes very slow. Same is happening if you open popup for an empty ComboBox, then bind large collection and try to open popup again - it takes some seconds before popup opens.

Here is the XAML:

<ComboBox Name="cbBlah">
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

and the sample code for binding to reproduce the problem:

var list = new List<string>();
for (var i = 0; i < new Random().Next(9000, 10000); i++)
    list.Add(i.ToString());
cbBlah.ItemsSource = list;

I tried to make virtualizing stack panel to look like this:

<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />

but it doesn't help, seems VirtualizationMode is ignored so popup opens very fast only first time and then, each time after binding changes, it's very slow.

UPDATE: I thought about not binding new collection every time, but bind an ObservableCollection once and then just changing its content. Same thing, as soon as content of collection changes, opening a popup still takes several seconds :(

Alexey
  • 843
  • 1
  • 7
  • 9
  • take a look at these question that i answered http://stackoverflow.com/a/8555403/920384 – punker76 Dec 18 '11 at 22:39
  • I also found this lag when did filtering on a collection. I was binding ListBox to a filtered list and every time I altered filter and filtered list was replaced with a new one lag happened. So I just bound ListBox to a souce list and made a bool Visible property on items, and in ItemTemplate Collapsed hidden items. This worked like a charm. Even though it is not ideal it works instantenously. I'll try to work my way out with ObservableCollection. May be I can use it to bind to observable filtered list. But it will be really weird. – zORg Alex May 25 '18 at 09:25

4 Answers4

151

According to this blog: http://vbcity.com/blogs/xtab/archive/2009/12/15/wpf-using-a-virtualizingstackpanel-to-improve-combobox-performance.aspx

I've tested it with this code:

<ComboBox Name="cbBlah" ItemsSource="{Binding}">
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

It works fine for first time and next times. It's not necessary to code these lines:

<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" />
starball
  • 20,030
  • 7
  • 43
  • 238
Miguel
  • 1,526
  • 1
  • 10
  • 3
  • 2
    You, sir, are a genius! This made a HUGE performance difference for me, and the fix is elegant and simple. Thanks! :) – dbeachy1 Feb 17 '15 at 06:40
  • 1
    Is it possible to achieve this with C# code? I'm implementing a class that is deriving from Combobox, and I would like to set this here. – jonas Aug 19 '15 at 11:20
  • Thanks this help me, too. You don't know how long i have tried to achieved this. Thank you!!! – Milan Kocic May 25 '17 at 11:14
  • I just wonder why is this not by default... How much does it cost ? In all cases, thank you very much, this has been bothering me for a while but I could not investigate this deep. – lollancf37 Jun 27 '19 at 11:08
  • 'It's not necessary to code these lines' --- do you mean it is this line of code that led to bad performance? because this is the only difference between your answer and the question's code. – Lei Yang Aug 12 '20 at 14:06
14

I had the issue with slow performance as well. But I had created a class that inherited form Combobox, therefor I would like to do this programmatically. So here is that solution for other googlers out there.

ItemsPanel = new ItemsPanelTemplate();
var stackPanelTemplate = new FrameworkElementFactory(typeof (VirtualizingStackPanel));
ItemsPanel.VisualTree = stackPanelTemplate;
jonas
  • 1,592
  • 3
  • 20
  • 29
  • Smashed that code into the constructor, worked beautifully. Cheers! – Kris Oct 28 '15 at 02:33
  • 1
    I'm aware that this is an old post, but for fellow googlers, this right here was an elegant solution. Speeds things up _tremendously_. – Chris Dec 27 '17 at 16:28
1

I just ran into this issue as well. I'm using this code in a custom combo box with a style template. When I ran my code in VS debugging mode the virtualization did not work properly. Once I ran it outside of debugging I can switch the content of the ObservableCollection without locking the UI up. It also might help if you set a max height and max width.

<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>
Chris
  • 5,882
  • 2
  • 32
  • 57
Phillip
  • 11
  • 1
0

From a usability perspective, using a standard combobox with more items than will fit on the screen is always cumbersome. It requires at least a textbox filter. In many cases the options can be prefiltered (e.g. by department, by first letter or range) that creates less objects and usualy is more user friendly.