Shady Boshra
Shady Boshra

Reputation: 2931

ComboBox DataSource Dynamically Change

First: I have List that filled up with object type of Customer

List<Customer> Customers = new List<Customer>();

Second: I have 2 ComboBox, one for Customer Name and other for Customer Phone. And set both DataSource to Customers list.

CustomersPhone_ComBx.DataSource = Customers.Select(Customer => Customer.Phone).ToList();
CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();

Third: When the user change selected item in Phone ComboBox, I want to filter the names in Name ComboBox Which is have the same Phone number it was selected. (Because it may be the phone number registered with more than one name). I have this code

private void CustomersPhone_ComBx_Leave(object sender, EventArgs e)
{
    if (CustomersPhone_ComBx.Text != "")
         CustomersName_ComBx.DataSource = Customers.Where(Customer => Customer.Phone == CustomersPhone_ComBx.Text).Select(Customer => Customer.Name).ToList();
    else
         CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();
}

But when I test it and change selected item in Phone ComboBox nothing change in Name ComboBox.


Update 1

Fourth: If I used foreach, it works fine like the following code, but don't work with DataSource

private void CustomersPhone_ComBx_SelectedIndexChanged(object sender, EventArgs e)
{
    List<Customer> FilteredCustomers = Customers
                       .Where(Customer => Customer.Phone == CustomersPhone_ComBx.Text).ToList();

    foreach (Customer C in FilteredCustomers)
        CustomersName_ComBx.Items.Add(C.Name);
}

Update 2

Theses properties of 2 ComboBox, they are the same.

        // CustomersName_ComBx
        // 
        this.CustomersName_ComBx.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
        this.CustomersName_ComBx.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
        this.CustomersName_ComBx.Dock = System.Windows.Forms.DockStyle.Fill;
        this.CustomersName_ComBx.Font = new System.Drawing.Font("Janna LT", 12F, System.Drawing.FontStyle.Bold);
        this.CustomersName_ComBx.FormattingEnabled = true;
        this.CustomersName_ComBx.Location = new System.Drawing.Point(0, 65);
        this.CustomersName_ComBx.Margin = new System.Windows.Forms.Padding(0, 7, 0, 0);
        this.CustomersName_ComBx.Name = "CustomersName_ComBx";
        this.CustomersName_ComBx.Size = new System.Drawing.Size(583, 46);
        this.CustomersName_ComBx.Sorted = true;
        this.CustomersName_ComBx.TabIndex = 52;
        // 
        // CustomersPhone_ComBx
        // 
        this.CustomersPhone_ComBx.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.SuggestAppend;
        this.CustomersPhone_ComBx.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
        this.CustomersPhone_ComBx.Dock = System.Windows.Forms.DockStyle.Fill;
        this.CustomersPhone_ComBx.Font = new System.Drawing.Font("Janna LT", 12F, System.Drawing.FontStyle.Bold);
        this.CustomersPhone_ComBx.FormattingEnabled = true;
        this.CustomersPhone_ComBx.Location = new System.Drawing.Point(0, 7);
        this.CustomersPhone_ComBx.Margin = new System.Windows.Forms.Padding(0, 7, 0, 0);
        this.CustomersPhone_ComBx.Name = "CustomersPhone_ComBx";
        this.CustomersPhone_ComBx.Size = new System.Drawing.Size(583, 46);
        this.CustomersPhone_ComBx.Sorted = true;
        this.CustomersPhone_ComBx.TabIndex = 51;
        this.CustomersPhone_ComBx.SelectedIndexChanged += new System.EventHandler(this.CustomersPhone_ComBx_SelectedIndexChanged);

And the form named NewReceipt_Delivery_Form and these line how I show it.

NewReceipt_Delivery_Form NewReceipt_Delivery_Form = new NewReceipt_Delivery_Form();
NewReceipt_Delivery_Form.ChangeUI();
NewReceipt_Delivery_Form.ShowDialog();

And ChangeUI method

private List<Customer> Customers = new List<Customer>();
public void ChangeUI()
{
    OleDbCommand SelectCustomersCMD = new OleDbCommand("SELECT * FROM Customers ORDER BY [ID] ASC", Program.GeneralConnection);
    OleDbDataReader SelectCustomersREAD = SelectCustomersCMD.ExecuteReader();
    while (SelectCustomersREAD.Read())
    {
        Customer Customer = new Customer();
        Customer.ID = Convert.ToInt32(SelectCustomersREAD[0].ToString());
        /* And so on. */
        Customers.Add(Customer);
    }
    SelectCustomersREAD.Close();
    CustomersPhone_ComBx.DataSource = Customers.Select(Customer => Customer.Phone).ToList();
    CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();
    CustomersPhone_ComBx.SelectedIndex = -1;
    CustomersName_ComBx.SelectedIndex = -1;
}

And the SelectedIndexChanged event as Aleksandar's answer

private void CustomersPhone_ComBx_SelectedIndexChanged(object sender, EventArgs e)
{
    string selectedPhone = (string)CustomersPhone_ComBx.SelectedItem;
    if (!String.IsNullOrEmpty(selectedPhone))
        CustomersName_ComBx.DataSource = Customers.Where(Customer => Customer.Phone == selectedPhone).Select(Customer => Customer.Name).ToList();
    else
        CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();
}

Upvotes: 3

Views: 5321

Answers (2)

Mong Zhu
Mong Zhu

Reputation: 23732

With the new information about the properties of the comboboxes I could finally reproduce your problem. It seems that the Sorted property is causing this problem:

this.CustomersName_ComBx.Sorted = true;

The solution is to set it to false

this.CustomersName_ComBx.Sorted = false;

to have the display still ordered, simply use OrderBy when you initialize the DataSource:

CustomersPhone_ComBx.DataSource = Customers.Select(Customer => Customer.Phone)
                                           .OrderBy(x=>x).Distinct().ToList();
CustomersName_ComBx.DataSource  = Customers.Select(Customer => Customer.Name)
                                           .OrderBy(x => x).ToList();

A (sort of explanation) can be found on the documentation of the property Sorted

In the Remarks section it says:

This property specifies whether the ComboBox sorts existing entries and add new entries to the appropriate sorted position in the list. You can use this property to automatically sort items in a ComboBox. As items are added to a sorted ComboBox, the items are moved to the appropriate location in the sorted list.

I guess when you change the DataSource the new element are simply put at the positions of the old ones. But the actual DataSource has now less elements. You can check it in the debugger. The count of DataSource will be less after the filtered collection has been assigned!

My second guess would be that if you set Sorted = true it actually ignores the DataSource collection after the initial initialization and from this moment on it only displays what is in the Items collection. The debugger shows that Items.Count remains the same after the filtered DataSource has been assigned, but DataSource.Count has changed. This seems some sort of an ambiguous state. You cannot modify the Items collection because the ComboBox is data bound but you cannot display the DataSource because the Sorted is set to true. Actually the remarks say that:

Attempting to set the Sorted property on a data-bound control raises an ArgumentException. You must sort the data using the underlying data model.

But it does not say what happens when you set it before the binding!

Upvotes: 3

Aleksandar
Aleksandar

Reputation: 266

Here is solution that worked for me, only difference is that i've used CustomersPhone_ComBx.SelectedItem instead of its text property.

 public partial class Form1 : Form
 {
    public List<Customer> Customers { get; set; } = new List<Customer>
    {
        new Customer
        {
            Name = "John",
            Phone = "123"
        },
        new Customer
        {
            Name = "Mary",
            Phone = "123"
        },
        new Customer
        {
            Name = "Peter",
            Phone = "555"
        },
        new Customer
        {
            Name = "George",
            Phone = "222"
        },
        new Customer
        {
            Name = "Christine",
            Phone = "555"
        }
    };

    public Form1()
    {
        InitializeComponent();
        CustomersPhone_ComBx.DataSource = Customers.Select(Customer => Customer.Phone).ToList();
        CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();
    }

    private void CustomersPhone_ComBx_SelectedIndexChanged(object sender, EventArgs e)
    {
        string selectedPhone = (string) CustomersPhone_ComBx.SelectedItem;
        if (!String.IsNullOrEmpty(selectedPhone))
            CustomersName_ComBx.DataSource = Customers.Where(Customer => Customer.Phone == selectedPhone).Select(Customer => Customer.Name).ToList();
        else
            CustomersName_ComBx.DataSource = Customers.Select(Customer => Customer.Name).ToList();
    }
}

public class Customer
{
    public string Name { get; set; }
    public string Phone { get; set; }
}

Upvotes: 0

Related Questions