10
votes

I have a ComboBox that is bound to a DataSource. I want to dynamically add items to the ComboBox based on certain conditions. So what I've done is add the options to a new list, and then change the DataSource of the ComboBox like so:

cbo.DataSource = null;
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";

Then, I check cbo.Items.Count, and it has not incremented - it does not equal the count of the DataSource. Any ideas what I can do here?

Note this is WinForms and not ASP.NET.

14
Did you check the .Count property before or after the dropdown was actually dropped down? I suspect there's some optimizations being done to avoid reloading the items collection too often, so it might postpone the whole thing until you actually drop it down on-screen. - Lasse V. Karlsen
Which version of .NET is this? In Visual Studio 2008, compiling for either 2.0 or 3.5 produces the correct number of items at once. - Lasse V. Karlsen
I get this problem in VS2012 with a dynamically created combobox - Kirsten Greed

14 Answers

12
votes

Did you check the Count immediately or at a later time? There is the possibility that the ComboBox does not actually update it's contents until there is an operation such as a UI refresh and hence the count will be off until that time.

On case where this may happen is if you update the DataSource before the Handle is created for the ComboBox. I dug through the code a bit on reflector and it appears the items will not be updated in this case until the ComboBox is actually created and rendered.

7
votes

If anyone experiences this problem on a dynamically added combobox, the answer is to ensure that you add the combobox to the controls of a container in the form.

By adding "this.Controls.Add(cbo);" to the code before setting the datasource, the problem goes away.

5
votes

I've found the cause...

I took out the cbo.Datasource = null line.. and added a cbo.Invalidate() at the end. This has solved the problem.

Thanks all for the advice.

4
votes
cbo.DataSource = null; 
cbo.DataSource = cbos; 
cbo.DisplayMember = "Title"; 
cbo.ValueMember = "Value"; 

Now before setting cbo.SelectedValue, or relying on Items to be up-to-date, call

cbo.CreateControl ;

and Items will be re-calculated.

The problem is that SelectedValue/SelectedIndex, which are WinForms properties, only accept the values that are legal according to the Items list, but that one is built only after GUI interaction, i.e. after instantiating a "real" Windows GUI combo box, i.e. after obtaining a Windows handle for the combobox.

CreateControl forces the creation of the Windows handle, no matter what.

1
votes
 ComboBox cbNew = new ComboBox();
    cbNew.Name = "cbLine" + (i+1);
    cbNew.Size = cbLine1.Size;
    cbNew.Location = new Point(cbLine1.Location.X, cbLine1.Location.Y + 26*i);
    cbNew.Enabled = false;
    cbNew.DropDownStyle = ComboBoxStyle.DropDownList;
    cbNew.DataSource = DBLayer.GetTeams(lineName).Tables[0];
    cbNew.DisplayMember = "teamdesc";
    cbNew.ValueMember = "id";
    Console.WriteLine("ComboBox {0}, itemcount={1}", cbNew.Name, cbNew.Items.Count);
        // The output displays itemcount = 0 for run-time created controls
        // and >0 for controls created at design-time
    gbLines.Controls.Add(cbNew);

TO

 ComboBox cbNew = new ComboBox();
    cbNew.Name = "cbLine" + (i+1);
    cbNew.Size = cbLine1.Size;
    cbNew.Location = new Point(cbLine1.Location.X, cbLine1.Location.Y + 26*i);
    cbNew.Enabled = false;
    cbNew.DropDownStyle = ComboBoxStyle.DropDownList;
    Console.WriteLine("ComboBox {0}, itemcount={1}", cbNew.Name, cbNew.Items.Count);
        // The output displays itemcount = 0 for run-time created controls
        // and >0 for controls created at design-time
    gbLines.Controls.Add(cbNew);
    cbNew.DataSource = DBLayer.GetTeams(lineName).Tables[0];
    cbNew.DisplayMember = "teamdesc";
    cbNew.ValueMember = "id";

The DataSource, DisplayMember and ValueMember property must be set after the control has been added to its container.

1
votes
By adding "this.Controls.Add(cbo);" to the code before setting the datasource, the problem goes away.   
 //Create dynamic combobox and add to Panel
    ComboBox ddCombo = new ComboBox();
    controls = new Control[1] { ddCombo };
    panel.Controls.AddRange(controls); 

    //After creating add to table layout
    tableLayoutPanel.Controls.Add(panel, 0, 0);

    ddCombo .Name = "ddName";
    ddCombo .Width = 200;
    ddCombo .Location = new Point(x, y);                                                                                                          ddCombo .DataSource = ds;//or any List
    ddCombo .SelectedIndex = ddCombo .Items.Count - 1;
0
votes

Just to clarify are you calling the count() method After calling the databind() method

0
votes

This code produces 2 in the message box for me, can you try it and see how it behaves for you?

You can paste it into a console application, and add a reference to System.Windows.Forms and System.Drawing.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;

namespace SO887803
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.Run(new MainForm());
        }
    }

    public partial class MainForm : Form
    {
        private Button _Button;
        private ComboBox _ComboBox;

        public MainForm()
        {
            _Button = new Button();
            _Button.Text = "Test";
            _Button.Location = new Point(8, 8);
            _Button.Click += _Button_Click;
            Controls.Add(_Button);

            _ComboBox = new ComboBox();
            _ComboBox.Location = new Point(8, 40);
            Controls.Add(_ComboBox);
        }

        private void _Button_Click(object sender, EventArgs e)
        {
            List<Item> items = new List<Item>();
            items.Add(new Item("A", "a"));
            items.Add(new Item("B", "b"));

            _ComboBox.DataSource = null;
            _ComboBox.DataSource = items;
            _ComboBox.DisplayMember = "Title";
            _ComboBox.ValueMember = "Value";
            MessageBox.Show("count: " + _ComboBox.Items.Count);
        }

        public class Item
        {
            public String Title { get; set; }
            public String Value { get; set; }
            public Item(String title, String value)
            {
                Title = title;
                Value = value;
            }
        }
    }
}
0
votes

comboBox1.DataSource=somelist;

int c1=comboBox1.DataSource.Count; // still zero

BindingContext dummy = this.comboBox1.BindingContext;// Force update NOW!

int c2=comboBox1.DataSource.Count; // now it equals somelist.Count

0
votes

I had the same problem (Im working with VS 2005).

What you need to do is set the DataSource to null, clear the items, reassign the datasource , display and value members.

Eg

cbo.DataSource = null;

cbo.Items.Clear();

cbo.DataSource = cbos;

cbo.DisplayMember = "Title";

cbo.ValueMember = "Value";

0
votes

Old thread, but I tried some of these solutions, along with suspending/resuming the bindingcontext, binding to and resetting a binding source, and just plain reloading the form. None worked to update my control with the newly bound data at the time of my .datasource setting (my items.count was empty, just like the OP).

Then I realized that my combobox was on a tabpage that was getting removed at the start of the code, and later re-added (after my databinding). The binding event did not occur until the tabpage was re-added.

Seems obvious in retrospect, but it was very difficult to detect at runtime, due to the order of calls and inability to see when things were changing.

0
votes

Try this code.

cbo.BindingContext = new BindingContext();
cbo.DataSource = null;
cbo.DataSource = cbos;
cbo.DisplayMember = "Title";
cbo.ValueMember = "Value";

Maybe your ComboBox`s BindingContext is null.

-1
votes

Ba salam,

you can simply refresh the UI by preformLayout() function;

Example:

comboBox1.performLayout();

regards mohsen s

-1
votes

please try this:

cbo.Parent = <your panel control>;
cbo.DataSource = null; 
cbo.DataSource = cbos; cbo.DisplayMember = "Title"; 
cbo.ValueMember = "Value";
MessageBox.Show(string.Format("itemcount is {0}", cbo.Items.Count);

I think your question sames like I met today.