Ivan-Mark Debono
Ivan-Mark Debono

Reputation: 16340

Why is the combobox doubling its items when being created?

I'm creating a combobox programmatically as follows:

var cbo = new ComboBox();
cbo.HandleCreated += (s, e) =>
{
    cbo.DataSource = mylist;
};

When I do the above, the combobox displays double the items contained in the list. However, when I do the following, the combobox displays the correct number of items:

var cbo = new ComboBox() {
    DataSource = mylist
};

Why does this happen?

Upvotes: 2

Views: 717

Answers (2)

Reza Aghaei
Reza Aghaei

Reputation: 125312

Reason

The HandleCreated raises just once. the problem is something else, it's because of the way that OnHandleCreated and data-binding has implemented in ComboBox.

This is how OnHandleCreated method of the ComboBox works:

  • It first raise HandleCreated event. (Keep in mind, IsHandleCreated is true at this point.)
  • Then for each item in Items collection of the control, sends a CB_ADDSTRING native message to add the item to native combo box.

And this is how setting DataSource works:

  • For each item in DataSource, it first adds the item to Items collection, then checks if IsHandleCreated is true, sends a CB_ADDSTRING native message to add the item to native combo box.

So when you set DataSource in HandleCreated event, for each item it sends CB_ADDSTRING native message twice.

That's why you see items twice in the drop-down and at the same time Items.Count shows correct count. Also it you click on additional item (the last half of the items) you will receive an index out of range exception.

Solution

To solve the problem, you can use either of the following optoins:

  1. You can delay the the HandleCreated event code execution by using BeginInvoke

  2. As another option you can rely on VisibleChanged event.

Option 1 - HandleCreated + BeginInvoke

var mylist = Enumerable.Range(1, 5).ToList();
var myvalue = 2;
var cbo = new ComboBox();
cbo.HandleCreated += (obj, args) =>
{
    BeginInvoke(new Action(() =>
    {
        cbo.DataSource = mylist;
        cbo.SelectedIndex = mylist.IndexOf(myvalue);
    }));
};
this.Controls.Add(cbo);

Option 2 - VisibleChanged

var mylist = Enumerable.Range(1, 5).ToList();
var myvalue = 2;
var cbo = new ComboBox();
cbo.VisibleChanged+= (obj, args) =>
{
    cbo.DataSource = mylist;
    cbo.SelectedIndex = mylist.IndexOf(myvalue);
};
this.Controls.Add(cbo);

Upvotes: 3

JC Borlagdan
JC Borlagdan

Reputation: 3638

This code duplicates the record, because on your initialization of combobox it will hit the event HandleCreated which will add your list to the combobox. Then after finishing the initialization, the execution of the code will hit the event HandleCreated again since the first one that execute that event is from initialization, and the second one is from the run time execution itself.

var cbo = new ComboBox(); //initialization
//below this comment is the event
    cbo.HandleCreated += (s, e) =>
    {
        cbo.DataSource = mylist;
    };

P.S.

There's a better way on populating comboboxes in C# WinForms; every control (combobox, textbox, etc.) have their different events. You can check on that first rather than creating your event.

Upvotes: 3

Related Questions