0

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 ;).

Steven Rands
  • 4,379
  • 3
  • 16
  • 46
Wiesio Pie
  • 123
  • 2
  • 5
  • There's always some binding and container generation overhead when using a **ListBox** but 10 seconds seems a little extreme even when there are around 8000 items in your list. Try removing your data template temporarily and see if that speeds things up. If so, the problem will be something to do with your data template. – Steven Rands Mar 05 '15 at 10:59
  • I agree with @StevenRands This is probably a DataTemplate problem. Perhaps you could simplify or consider whether it's necessary to display 8000 items in your list at the same time. – Mike Eason Mar 05 '15 at 11:09
  • Thank You for Your reply! :) Unfortunatelly, it didn't help :/. Even without using DataTemplate it takes 10-12 seconds. I need all records to be shown because I'm using TextBox to filter data. So without any string inside textBox it should show all data and filter them as user writes some letters. By the way, filtering data (using ICollectionView) with the first and second letter takes a long time as well (few seconds, 5-6) and as the results shrink it becomes fast enough. – Wiesio Pie Mar 05 '15 at 11:38

1 Answers1

3

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.

Community
  • 1
  • 1
Liero
  • 19,054
  • 16
  • 100
  • 195
  • I like the indepth explanation of how Listboxes virtualize their content. Thank you! Did not know this before – Xeun Mar 05 '15 at 11:50
  • Thank You! It does work. I'm using "ReuxablesLegacy" free wpf style (I didn't mention it, I thought it's unimportant). After turning it off all started to work as it should. It seems they're using some other ItemsPanel, much slower in this case. Thank You very much! :). – Wiesio Pie Mar 05 '15 at 11:57
  • 1
    @Xeun: notice, that you can go even further. This is called UI virtualization, but you could do also Data Virtualization. That means, that you download only those data from you database, or webservice, which are currently visible. Basically you create your own implementation of IList interface, that loads items only when they are accessed by wpf: http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization – Liero Mar 05 '15 at 11:57
  • Sure! I'd like to but I don't know how :/. :P Tick it? – Wiesio Pie Mar 05 '15 at 13:53