Listbox DataSource not working when added to a ToolStripDropDown

1k Views Asked by At

I have been struggling all day with this problem, although I couldn't find a solution. I apologize for the long post, I tried to be concise and clear.

This is what works: I create a Form and inside its class I create a ListBox dynamically and set its DataSource to a DataTable as follows:

public partial class FrmAddress : Form
{
    private ListBox listBox1 = new ListBox();

    public FrmAddress()
    {
        this.InitializeComponent();

        [...]

        this.Controls.Add(listBox1);
    }

    [...]

    private void Load_Countries()
    {
        this.listBox1.DataSource = dtCountries;
        this.listBox1.DisplayMember = "Country";
        this.listBox1.ValueMember = "Country_ID";
    }

    [...]
}

This does not work: Create a custom control (inherited from ToolStripDown), create a new instance of ToolStripControlHost(listBox1), add that instance to the ToolStripDown. Set the listBox1.DataSource to a DataTable. When ToolStripDown is show, the listbox is there but empty (not showing the datasource content).

public class CtlDropdownPopup : ToolStripDropDown
{
    ListBox controlToPop;
    ToolStripControlHost controlHost;

    public CtlDropdownPopup(ListBox controlToPop)
    {
        this.controlToPop = controlToPop;
        this.controlToPop.Location = Point.Empty;

        this.controlHost = new ToolStripControlHost(this.controlToPop);

        [...]

        this.Items.Add(this.controlHost);
    }
}

public class CtlCombobox : ComboBox
{
    private readonly CtlDropdownPopup suggestionDropDown;
    private readonly ListBox suggestionList = new ListBox();

    public CtlCombobox()
    {
        this.suggestionDropDown = new CtlDropdownPopup(this.suggestionList);
    }

    public void Source(DataTable dt, string display, string value)
    {
        this.suggestionDT = dt;

        this.suggestionList.DataSource = dt;
        this.suggestionList.DisplayMember = display;
        this.suggestionList.ValueMember = value;
    }
}

The custom CtlDropdownPopup is called like: (simplified)

private CtlCombobox LstCountry;
this.LstCountry.Source(dtCountries, "Country", "Country_ID");

As I said, the ToolStripDropDown is shown with the listBox1 in it, but the list is empty. Curiously if I modify the Source() method to

    public void Source(DataTable dt, string display, string value)
    {
        this.suggestionDT = dt;

        // this.suggestionList.DataSource = dt;
        // this.suggestionList.DisplayMember = display;
        // this.suggestionList.ValueMember = value;

        if (this.suggestionList != null)
        {
            foreach (DataRow row in dt.Rows)
            {
                this.suggestionList.Items.Add(row[display].ToString());
            }
        }
    }

The listbox is shown with the items on it. Although this workaround does the job, it is annoying not finding the answer of why can't I set the DataSource directly (as I did directly in the first example), but manually having to add the items.

Any ideas will really help me to sleep well tonight :)

Thought #1: I believe since the same dtCountries is linked to other ComboBox1.DataSource, that may be the problem, so I set this.controlToPop.DataSource = dt.Copy(); in hopes "it is not somehow linked to the combobox", but the problem presists.

Side note: I am trying to create a custom combobox, that suggest items within the DataTable.

Idea from https://www.codeproject.com/Tips/789705/Create-combobox-with-search-and-suggest-list

1

There are 1 best solutions below

2
41686d6564 On BEST ANSWER

You need to set the BindingContext property of the ListBox.

When a ListBox (or any control) is added onto the form, it inherits its BindingContext property from the form. Now since you're adding the ListBox onto another TopLevel control with .BindingContext == null, it doesn't inherit that property from the form, hence, it has no BindingContext.

You can simply avoid this problem by creating a new BindingContext for the ListBox:

public void Source(DataTable dt, string display, string value)
{
    this.suggestionDT = dt;

    this.suggestionList.BindingContext = new BindingContext();  // <<<<<<<<<<<<<
    this.suggestionList.DataSource = dt;
    this.suggestionList.DisplayMember = display;
    this.suggestionList.ValueMember = value;
}

You can also copy the BindingContext from the form instead (either through the CtlCombobox control or by passing it as a parameter).

Hope that helps.