1

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

Ricardo González
  • 1,385
  • 10
  • 19
  • `this.controlToPop.DataSource` Does this compile without casting? – 41686d6564 stands w. Palestine May 13 '18 at 03:49
  • @AhmedAbdelhameed yes – Ricardo González May 13 '18 at 03:50
  • Are you sure?! Because the `Control` class [doesn't have a `DataSource` property](https://msdn.microsoft.com/en-us/library/system.windows.forms.control_properties(v=vs.110).aspx). You should be getting a `'Control' does not contain a definition for 'DataSource'...` error. Same for `DisplayMember` and `ValueMember`! – 41686d6564 stands w. Palestine May 13 '18 at 03:55
  • `Control` is the base class for all the controls of windows form but it does not have DataSource property. Only specific control have DataSource property such as DataGridView, Combobox, Listbox etc. So if you want to generalize the databinding logic using Control object, you can not do it. – Chetan May 13 '18 at 04:04
  • @AhmedAbdelhameed you are right. I oversimplified the code in this post, but I am setting the DataTable to the a ListBox, I updated the code. – Ricardo González May 13 '18 at 04:17
  • @ChetanRanpariya you are right, I oversimplified the code, although I updated the post to reflect what I have and that I am setting the Listbox DataSource property – Ricardo González May 13 '18 at 04:20

1 Answers1

2

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.