Comboboxes Are Linked for Some Reason

Comboboxes are linked for some reason

New much improved solution:

A DataSource is more than just the data.

There is hidden default BindingSource that makes the ComboBoxes follow.

To avoid this coupling and also the data replication in the first version of this answer, all you need to do is create a separate BindingSource for each ComboBox. These share the DataTable but have each its own rowPointer:

BindingSource bS1, bS2, bS3;
..
..
..
..
dt = new DataTable();
dt.Load(reader);

bS1 = new BindingSource();
bS1.DataSource = dt;
bS2 = new BindingSource();
bS2.DataSource = dt;
bS3 = new BindingSource();
bS3.DataSource = dt;
..
ddl1.DataSource = bS1 ;
ddl2.DataSource = bS2 ;
ddl3.DataSource = bS3 ;
..
..

Now the ComboBoxes can be changed independently.

Note: My first version worked but was the wrong way do it. Sorry..!

ComboBoxes are linked (and that is bad)

You are running across something that is present in the background of WinForms called the "CurrencyManager".

Its job is to synchronize the "current record" across all bindable controls that refer to the same data source.

For instance, if you had added a label and bound it to the same list, and bound it so that it shows a property of one of the objects, it would always show the property value of the same object you had selected in the combobox.

One bonus of this is that you could easily add a form that edits a list of objects, binding textboxes and such to the properties of one of the objects and adding a navigator that allows you to move to the next or previous row. You would not have to manually ensure all textboxes refer to the correct object, the CurrencyManager would do all this for you.

However, in your case, since you bound the same data source to all three comboboxes, the CurrencyManager will ensure all three select the same row. If you select a new row in one of the comboboxes, the CurrencyManager will go and fix all the others to refer to the same row.

You can fix this in various ways:

  1. You can override the binding context for each combobox:

    comboBox2nd.BindingContext = new BindingContext();
    comboBox3rd.BindingContext = new BindingContext();

    Note that if you go this route you need to do this before assigning the SelectedIndex or SelectedItem properties, otherwise the CurrencyManager will have updated the other two comboboxes before you assigned new BindingContexts.

  2. You can assign distinct data sources to each combobox:

    combobox2nd.DataSource = testList.ToList();
    combobox3rd.DataSource = testList.ToList();

Bind multiple ComboBox to a single List - Issue: When I select an item, all combo boxes change

Since you are binding all combo boxes to the same data source - a single list - they are using a single BindingManagerBase.

So when you choose an item from one of combo boxes, the current Position of the shared binding manager base changes and all combo boxes goes to that position of their shared data source.

To solve the problem you can bind them to different data source:

  • You can bind them to yourList.ToList() or any other list for example different BindingList<T>.

    combo1.DataSource = yourList.ToList();
    combo2.DataSource = yourList.ToList();
  • You can use different BindingSource for them and set your list as DataSource of BindingSource

    combo1.DataSource = new BindingSource { DataSource= yourList};
    combo2.DataSource = new BindingSource { DataSource= yourList};

Also as another option:

  • You can use different BindingContext for your combo boxes. This way even when you bind them to a single list, they are not sync anymore.

    combo1.BindingContext = new BindingContext();
    combo1.DataSource = yourList;
    combo2.BindingContext = new BindingContext();
    combo2.DataSource = yourList;

In fact all controls of the form use a shared BindingContext. When you bind 2 controls to a same data source, then they also use the same BindingManagerBase this way, when you for example move to next record, all controls move to next record an show value from bound property of next record. This is the same behavior that you are seeing from your combo boxes. Being sync for controls which are using the same BindingManagerBase is a desired behavior. Anyway sometimes we don't need such behavior. The post shares the reason and the solution.

Linking 2 combo boxes

There are several ways how to do this, but the easiest one and the one I use mostly is setting a different Row Source to the Combobox2 when the Change event (or AfterUpdate, depending on your needs) of Combobox1 is fired.

Example:

I have two tables

Animals
1 Dog
2 Cat
3 Mouse
4 Rabbit

Cars
1 Audi
2 BMW
3 Ferrari
4 Porsche
5 McLaren

On the form I have two comboboxes, the second one is based on the selection of the first one, which contains just two options: Animals, Cars.

Sample code:

Private Sub Combo1_Change()
Dim cmb1 As ComboBox: Set cmb1 = Me.Combo1
Dim cmb2 As ComboBox: Set cmb2 = Me.Combo2

Select Case cmb1.Value
Case "Animals"
cmb2.RowSource = "Animals" ' Table name Animals
Case "Cars"
cmb2.RowSource = "SELECT TOP 3 * FROM Cars" ' SQL command to table Cars
Case Else
cmb2.RowSource = "Animals"
End Select
End Sub

Now each time the value in Combo1 changes, so does the rowsource of Combo2.

Note: You need to set a default rowsource of Combo2 based on the value in Combo1 on form load, so the Combo2 is not empty on start.

the mechanic beneath combobox with same datasource

This behavior is implemented by the BindingContext Class. The crucial part is:

For each data source on a Windows Form, there is a single CurrencyManager or PropertyManager.

If you set a ComboBox's (or ListControl's) datasource, it registers itself to the containing Control's BindingContext so it will communicate with its CurrencyManager. When two ListControls register with the same data source, they receive the same CurrencyManager.

You can simply check this by

this.comboBox1.BindingContext[lst].CurrentChanged += Form1_CurrentChanged;
this.comboBox2.BindingContext[lst].CurrentChanged += Form1_CurrentChanged;
this.BindingContext[lst].CurrentChanged += Form1_CurrentChanged;

void Form1_CurrentChanged(object sender, EventArgs e)
{
Debug.WriteLine(((CurrencyManager)sender).GetHashCode());
}

This will show three identical hashcodes in the output window if you change the selected item of one combo box.

This also shows why there is no "shortcut" to a RadioButton's value, because it doesn't communicate with the CurrencyManager (it's not a ListControl). You'll have to write code to sync its value with a combo box's current value.

Combobox Data Binding (independent choice)

The following code does work seamlessly for me:

BindingList<Customer> customer = new BindingList<Customer>();
customer.Add(new Customer(1, "Mike"));
customer.Add(new Customer(2, "Max"));
customer.Add(new Customer(3, "Taylor"));

BindingList<Customer> customer2 = new BindingList<Customer>(customer);

combobox1.DisplayMember = "Name";
combobox1.ValueMember = "Id";
combobox1.DataSource = customer;

combobox2.DisplayMember = "Name";
combobox2.ValueMember = "Id";
combobox2.DataSource = customer2;

Multiple Combo Boxes With The Same Data Source (C#)

I know you didn't ask for it but may I suggest you to refactor your final code a little bit :-)

private List<TSPrice> GetPriceList()
{
return new List<TSPrice>
{
new TSPrice(0, ""),
new TSPrice(0, "Half Day"),
new TSPrice(0, "Full Day"),
new TSPrice(0, "1 + Half"),
new TSPrice(0, "2 Days"),
new TSPrice(0, "Formal Quote Required")
};
}

private void BindPriceList(ComboBox comboBox, List<TSPrice> priceList)
{
comboBox.DataSource = priceList();
comboBox.ValueMember = "Price";
comboBox.DisplayMember = "Description";
comboBox.SelectedIndex = 0;
}

BindPriceList(objInsuredPrice, GetPriceList());
BindPriceList(objTPPrice, GetPriceList());
BindPriceList(objProvSum, GetPriceList());

how to link comboboxes in vb.net

This should do it:

  private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
comboBox2.SelectedItem = comboBox1.SelectedItem;
}

EDIT for VB.NET:

   Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
ComboBox2.SelectedItem = ComboBox1.SelectedItem
End Sub


Related Topics



Leave a reply



Submit