Search code examples
c#wpflistboxdatatemplatestopwatch

WPF ListBox.ItemsSource = ObservableCollection - takes a long time to load


I've got code like this: (loading Collection of custom objects into memory and into ListBox item)

public class Product : INotifyPropertyChanged
  {
    // these four doesn't matter, just Product's simple data
    public string nDB_No { get; set; }
    public string fdGrp_Cd { get; set; }
    public string long_Desc { get; set; }
    public int    refuse { get; set; }

    // I do not load this Collection right away, only after explicit call
    public ObservableCollection<Ingredient> ingredients { get; set; }

    public Product() {sets all null}
    public static ObservableCollection<Product> LoadProductsFromList(List<string> productList) {gets products data from SQLServer DB}
    // and other methods irrelevant here
  }



  private void buttonCreate_Click(object sender, RoutedEventArgs e)
  {
    ObservableCollection<Product> productCollection = new ObservableCollection<Product>();

    List<string> productList = getProductsNamesFromDB();

    var watch = Stopwatch.StartNew();
    productCollection = LoadProductsFromList(productList);
    watch.Stop();
    MessageBox.Show(watch.ElapsedMilliseconds);

    // At this point there is about 700-800ms - that's ok, there's over 8000 records in DB

    watch = Stopwatch.StartNew();
    listBox.ItemsSource = productCollection;
    watch.Stop();
    MessageBox.Show(watch.ElapsedMilliseconds);

    // At this point watch shows only about 500ms but it takes over 10 seconds 
    //to load the ListBox with data and to show the MessageBox.
  }

The listBox item has very simple DataTemplate attached, just one Rectangle, few colours and one TextBlock. When I place BreakPoints after attaching listBox.ItemsSource it breaks right away. So it seems like there's another thread ListBox is creating and it's doing something. I can't make up any faster way.

I'm doing something wrong but I don't know what it is. Please help ;).


Solution

  • By default, ListBox uses VirtualizingStackPanel as ItemsPanel, which means, that ItemsSource can contain practically unlimited ammount of items without performance effect.

    try this in a clean solution without listbox modifications. It shows million items without problem

    <ListBox x:Name="listBox" />
    listBox.ItemsSource = Enumerable.Range(0, 1000000).ToArray();
    

    VirtualizingStackPanel instantiates DataTemplate only for those items, which are currently visible in scrollviewer. It is called virtualization

    It seems, that you have somehow broken the virtualization. Reasons could be:

    • You have modified style or template of listbox so it does not use VirtualizingStackPanel
    • You have set ScrollViewer.CanContentScroll="False"
    • You have set IsVirtualizing="False"
    • see MSDN article 'Optimizing Performance' for more info

    Are you using any wpf theme, or some implicit styles for listbox?

    In case if ItemsControl, some work is required in order to make virtualization work: Virtualizing an ItemsControl?

    Just to make sure, it is virtualization problem, try to replace your datatemplate with simplest possible - empty rectangle, or even delete it. Hope this helps.