0

i'm trying to populate a comboBox with 50.000 items.

The problem is... it takes too much time to load all the items into the GUI.

So i'm trying to populate asyncronous:

First i create the datasource array:

  var source = Enumerable.Range(1, 50000).Select(e => new{ID = e}).ToArray();

Then i set the datasource property of the comboBox:

  BeginInvoke(new Action(() => comboBox1.DataSource = source));

The problem of the line of code above is taking a lot of time to complete and the Window is Freezing.

The workaround i'm using is set the datasource to null and add the item in a foreach statement and calling Application.DoEvents() each time:

        comboBox1.DataSource = null;
        foreach (var e in source)
        {
            comboBox1.Items.Add(e);
            Application.DoEvents();
        }

It works fine, but i need the comboBox to be bound with a datasource and in this way i can't.

How can I set the datasource an make it loads like the code above ?

George
  • 777
  • 7
  • 17
  • 2
    `i'm trying to populate a comboBox with 50.000 items.` That sounds like a really bad idea, both in terms of the user experience and in terms of UI performance. – Eric J. Sep 25 '15 at 01:55
  • @EricJ. i know, but the user needs this way for now. Actually is 5.000 items, but for test purpose i'm working with 50.000 to see the async way really working – George Sep 25 '15 at 01:59
  • 1
    Did you try setting virtualization to your UI control ? It hurts a bit on scrolling but the UI don't need to perform any action when binding data that's completely out of UI scope. – cscmh99 Sep 25 '15 at 02:13
  • 1
    Even 5 thousand items is bad, both in performance and usability. And the fact that you're jumping through hoops to try and get it to work should show you're going down the wrong path. @cscmh99 has the right idea. – Steve Wellens Sep 25 '15 at 02:14
  • @cscmh99 im using winforms, i dont know how to do this with a combobox. is there a way? – George Sep 25 '15 at 02:33
  • Regarding UI virtualization: Virtualization exists both for WPF (Example) and WinForms. But only some WinForms support it, like ListView, DataGridView, TreeView, etc. Those are the controls designed for bigger amounts of data. If possible you could switch to one of these controls. Or, we can try decomplie controls like DevExpress Or Telerik to see how they did the virtualisation for the winform combobox. i would imagine that's a custom control that consist of a TextBox (display current selecteditem) + Button (visible/collapsed the listView) + ListView underneath – cscmh99 Sep 25 '15 at 02:54
  • You can check my answer below but for amount of items you have suggestion is to create custom select control in which you can select a single item/value with search capability. Search by typing, by character, etc – T.S. Sep 25 '15 at 03:17
  • @cscmh99 thanks for the infos. i think im going to test textbox withautocomplete and then inherit to a control that support selecteditem, selected value etc for now – George Sep 25 '15 at 04:21
  • I've tested setting the data source of the combo box line, it takes ~1s for 50K items and ~100ms for 5K, which I definitely wouldn't call "too much time to load". Either your real case is different than the presented (thus may have a different problem) or you shouldn't have problem at all (as you mentioned in the comments, your real target is ~5K). – Ivan Stoev Sep 26 '15 at 12:45
  • @George Have you read my comment above? What are your times so you are concerned? Also, it's worth mentioning that I see no big benefit of binding a data source to the **list** portion of the combo - it creates more difficulties than solving something. Note that you still can data bind the selected value of the combo w/o binding the list part - they use different data sources anyway. – Ivan Stoev Sep 28 '15 at 10:35
  • @IvanStoev in my machine 5k itens takes 0.4, my client 1.0 ms. with 5 combos. 5 secs can be a pain. but your last part is intereting i didn`t know. i will search on google, thanks\ – George Sep 28 '15 at 14:39
  • @George I see. Then I strongly suggest you to consider not using data source for the list portion because believe me, there are standard ways of doing such things with data sources, but none of them works here because ComboBox has a terrible implementation for handling external data source modifications. If you remove the data source constraint from your question, I think I can help you finding something that best fits your needs. – Ivan Stoev Sep 28 '15 at 15:52

2 Answers2

0

Combo box with a 100 items is a bad idea but if you insist to populate 5000...

Get good old backgroundworker and wire it up to load datasource, whichever object it would be, lets say an array. In pseudo code (these methods may have arguments)

void DoWork() // handles loading of an array
{ 
    // load array here. cbo is disabled 
    myArrayOfValues = <whatever you get from DB>
    foreach var item in myArrayOfValues 
    {

        // here call `worker.Progress` and pass your item in argument
    }
}

void OnWorkComleted(....) 
{ 
   // Set your combo as disabled initially, so people can't click while it is loading


    cbo.Enabled = true;

}

void OnWorkerProgress(....) 
{ 
    // Add items one by one
    cbo.Items.Add(myValue);

}

I have not tested it but it should work. I like background worker for this because it solves a problem of control and background thread. Otherwise you would have to deal with if invokeRequired etc. Here this part of handling async jobs and controls is abstracted for you.

Adding items without Datasource

class Itm
{
    string A {get; set;}
    string B {get; set;}
    string C {get; set;}
}


void Setup()
{
    for (int i = 1; i < 4; i++)
    {
        cbo.Items.Add(new itm { a = "A" + i, b = "B" + i, c = "C" + i});
    }
    cbo.DisplayMember = "A";
    cbo.ValueMember = "B";
}


void OnSelectedIndexChanged(....)
{
     if (cbo.SelectedIndex > -1)
     {
         var item = (itm)cbo.SelectedItem;
         MessageBox.Show(string.Format("Selected: {0}, {1}, {2}", item.A, item.B, item.C));
     }
}
T.S.
  • 18,195
  • 11
  • 58
  • 78
  • thanks for your answer, but the array creation is fast, the problem is setting the datasource. thats the slow part... -> cbo.DataSource = myArrayOfValues; – George Sep 25 '15 at 04:18
  • @George Then here is the answer. Set datasource on the BG thread and your form will remain responsive. Or you saying that the code above freezes your form? – T.S. Sep 25 '15 at 13:28
  • yes, because cbo.DataSource = myArrayOfValues run in the main thead, even if i put in dowork it throws the cross-thread exception. and cbo.Invoke doesnt solve this because the setting occurs in the main thread without calling application.doevents internaly – George Sep 25 '15 at 13:52
  • @George Then, there is something that will not freeze your form. Check the update. This will not have datasource but it will add your items w/o freezing. Yes, you don't want to set combo on `doWork` because that method runs on a different thread. This is why you have `WorkCompleted` and `progress`. So, you may have to give up datasource property to load async and not freeze – T.S. Sep 27 '15 at 14:41
  • Hi T.S., but thats exactly what i dont want, i need the combo to be bound... and in my question, i already did foreach add with application.doevents ... that doesn't freeze my window...but i really need the datasource :( – George Sep 27 '15 at 18:54
  • @George Why does it need to be DS? – T.S. Sep 28 '15 at 00:35
  • Hi, because a lot of other routines uses the selectedvalue, displaymember and value member. and only works with a databound combobox – George Sep 28 '15 at 14:34
  • @George Please check my answer again - additions on the bottom. I don't think you realize that you absolutely **can** add items one by one and still use advanced properties of selected item. – T.S. Sep 28 '15 at 15:16
0

I am not sure if this will help in your case but make sure you always set DataSource last if you re setting DisplayMember and ValueMember. Makes a huge difference. More info here: Link