views:

559

answers:

4

I have set up a dialog with several tabs. One of these contains twenty combo boxes, each with over 100 items, added like this :

foreach (var x in collection)
{
    string text = FormatItem (x);
    combo.Items.Add (text);
}

so there is nothing fancy at all with the items. They are plain strings and the combo boxes get filled when the dialog is created. This happens almost instantenously.

However, when the user clicks on the tab containing all these combo boxes for the very first time, the GUI freezes for several seconds (and I am running on a really beefy machine).

I loaded the symbols for System.Windows.Forms and tried to break into the debugger while the program is stuck. What I have discovered is a stack trace with the following calls:

System.Windows.Forms.Control.CreateHandle()
System.Windows.Forms.ComboBox.CreateHandle()
System.Windows.Forms.Control.CreateControl(...) x 3
System.Windows.Forms.Control.SetVisibleCore(true)
System.Windows.Forms.TabPage.Visible.set(true)

which results in plenty of native transitions, WndProc calls, etc. I suppose this happens for every single item in every combo box. Phew.

Obviously, I cannot optimize WinForms. But maybe I can take some actions in order to avoid all this hell getting lose on my poor GUI? Any ideas?

Nota bene:

  1. I've no event handlers attached on the combo boxes which could be called when the controls get created for real.

  2. If I try to access the Handle property of the combo boxes just after having created and populated the form, I pay the penalty at that moment, rather than when the tab becomes visible for the first time. But having to wait several seconds when creating the form is not acceptable either. I really want to get rid of the long set up time.

  3. The idea of applying BeginUpdate and EndUpdate does not apply here: these should be used to prevent the control from repainting when its items list gets filled. But in my case, the problem happens well after the control has been set up.

A: 

What you're saying is not consistent with anything I ever observed... :s

But have you tried using .BeginUpdate / .EndUpdate ?

Another thing you coud try is not populate the boxes until needed. Delay it until the box gets focus for example... (If you trap the dropdown event some user might be annoyed that the up/down arrow keys won't work.)

danbystrom
Yeah, postponing the filling of the controls' items could work... but it requires quite a bit of tweaking, since I then cannot use the `SelectedIndex` and other such properties.
Pierre
A: 

Instead of iteration your collections, would't setting the ComboBox.DataSource be a viable, and much faster alternative?

comboBox1.DataSource = myCollection1;
comboBox2.DataSource = myCollection2;
comboBox3.DataSource = myCollection3;
// and so on...

Here is a more complete example:

public class Entity
    {
        public string Title { get; set; }
        public override string ToString()
        {
            return Title;
        }
    }
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            List<Entity> list = new List<Entity>
                                    {
                                        new Entity {Title = "Item1"},
                                        new Entity {Title = "Item2"},
                                        new Entity {Title = "Item3"}
                                    };

            comboBox1.DataSource = list;

        }
Marcus
A: 

Lots of controls on a form can be a problem. I once had a form that dynamically created between 50-100 textbox controls. It was slow to load.

We solved that problem by using a datagrid instead. It's a control that is optimized for lots of data. I don't know what your exact requirements are, but it might work.

Chris Holmes
A: 

Everything I tried failed up to now to speed up the first display of the tab containing all the combo boxes. Data binding didn't help either.

I finally decided to fix the issue by doing a trick, similar to what danbystrom proposed, i.e. only populate the Items collection when the focus first arrives on a combo. This still produces a noticeable delay, the time all items get created (within a BeginUpdate and EndUpdate pair of method calls), but it is tolerable (approx. 200ms versus several seconds in my original scenario).

Pierre