Question

I don't always post a problem to Stack Overflow, but when I do, I usually find the solution before I finish posting the problem. :-) Seriously now, I am experiencing a weird behavior of my BindingSource, I can't find a logical explanation for it and I need your help.

Using NET 4, reading a SQL database through EntityFramework 4, writing results to a list of ViewModels which are stored in a BindingList, which is then bound to a DataGridView via BindingSource. Under the DataGridView there are various fields such as check boxes, text fields and combo boxes bound to the same BindingSource as the DataGridView. That way, when you select an item from a DataGridView all those fields are updated with the currently selected DataGridView item.

Let's say there are two tables in a database which are defined like this:

Table name: Country
-------------------
ID: integer, PK
Name: nvarchar

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar

Let's also say there is a table in a database called "Citizen" defined like this:

Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)

The DataGridView is bound to a BindingList<CitizenViewModel> where "CitizenViewModel" is defined like this:

class CitizenViewModel
{
    public int ID { get; set; }
    public int CityID { get; set; }
    public string Name { get; set; }
    public int CountryID { get; set; }

    public CitizenViewModel(Citizen c)
    {
        this.ID = c.ID;
        this.CityID = c.CityID;
        this.Name = c.Name;
        this.CountryID = c.City.CountryID;
    }
}

Let's name the DGV's BindingSource citizenViewModelBindingSource.

There are two combo boxes on the form, cmbCountry and cmbCity, both bound to BindingSources of Country and City type respectively, with "DisplayMember" set to "Name" and "ValueMember" set to "ID" for both combo boxes. The SelectedValue property of cmbCountry is bound to the CountryID property of citizenViewModelBindingSource and the SelectedValue property of cmbCity is bound to CityID property of the same binding source, so the combo box displayed value changes according to the item selected in DGV.

I am handling the CurrentChanged event of countryBindingSource which is behind the cmbCountry so when the selected country is changed, cmbCity displays cities of that selected country.

private void countryBindingSource_CurrentChanged(object sender, EventArgs e)
{
    // Get the list of Cities that belong to the selected Country
    cityBindingSource.DataSource = GetCities(((Country)countryBindingSource.Current).ID);
}

The problem is as follows: let's say my result set has got 5 rows, where 2 of them have the same CountryID but different CityID, and the other 3 have all different CountryIDs and CityIDs. When I select one of the two items with the same CountryID and then select the other one with the same CountryID, the combo box with cities will update accordingly. However if I first select one of these two with the same CountryID, then select one of those with different CountryID and eventually select the other row with the same CountryID, the binding source will magically assign the same CityID as the previously selected row with the same CountryID. Confusing? I will explain with an example (and omit irrelevant fields).

Results:
1. Name: John Doe; Country: USA; City: Seattle
2. Name: John Smith; Country: Canada; City: Montreal
3. Name: Michael Owen; Country: England; City: Liverpool
4. Name: George Bush; Country: USA; City: Washington
5. Name: Vladimir Putin; Country: Russia; City: Moscow

Select John Doe, combo boxes say USA and Seattle.
Select George Bush, combo boxes say USA and Washington.
Select John Doe, combo boxes say USA and Seattle.
Select George Bush, combo boxes say USA and Washington. So everything is still fine.
Select Michael Owen, combo boxes say England and Liverpool.
Select John Doe, combo boxes say USA and Seattle.
Now watch this. Select George Bush, combo boxes say USA and Seattle (not USA and Washington as it should). The binding source has changed the CityID of George Bush!

I have finished writing about the problem but the solution hasn't come to my mind. Why is this happening and how to avoid this behavior?

EDIT

Solved the problem by unbinding the "SelectedValue" of cmbCity and modifying countryBindingSource_CurrentChanged function like this:

    private void countryBindingSource_CurrentChanged(object sender, EventArgs e)
    {
        if (citizenViewModelBindingSource.Current != null)
        {
            cityBindingSource.DataSource = GetCities(((Country)countryBindingSource.Current).ID);
            // update the combo box manually
            cmbCity.SelectedValue = ((CitizenViewModel)citizenViewModelBindingSource.Current).CityID;
        }
    }

But this seems like a hack to me and I will keep this question open if anyone has got a clue on why this would happen.

Was it helpful?

Solution

Solved the problem by unbinding the "SelectedValue" of cmbCity and setting the cmbCity.SelectedValue manually. Accomplished this by modifying countryBindingSource_CurrentChanged function like this:

    private void countryBindingSource_CurrentChanged(object sender, EventArgs e)
    {
        if (citizenViewModelBindingSource.Current != null)
        {
            cityBindingSource.DataSource = GetCities(((Country)countryBindingSource.Current).ID);
            // update the combo box manually
            cmbCity.SelectedValue = ((CitizenViewModel)citizenViewModelBindingSource.Current).CityID;
        }
    }

"I think you can't do anything else but moving away from data binding because changing the content of a combobox while it has data binding modifies the property bound to Selectedvalue, like it or not." – Gert Arnold

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top