C# 绑定源的意外行为

C# 绑定源的意外行为,c#,.net,winforms,bindingsource,C#,.net,Winforms,Bindingsource,我并不总是将问题发布到Stack Overflow,但当我发布问题时,我通常会在发布问题之前找到解决方案:说真的,我的BindingSource出现了一个奇怪的行为,我找不到一个合理的解释,我需要你的帮助 使用NET4,通过EntityFramework4读取SQL数据库,将结果写入存储在BindingList中的ViewModels列表,然后通过BindingSource将其绑定到DataGridView。在DataGridView下,有各种字段,如复选框、文本字段和组合框,它们与DataGr

我并不总是将问题发布到Stack Overflow,但当我发布问题时,我通常会在发布问题之前找到解决方案:说真的,我的BindingSource出现了一个奇怪的行为,我找不到一个合理的解释,我需要你的帮助

使用NET4,通过EntityFramework4读取SQL数据库,将结果写入存储在BindingList中的ViewModels列表,然后通过BindingSource将其绑定到DataGridView。在DataGridView下,有各种字段,如复选框、文本字段和组合框,它们与DataGridView绑定到同一个BindingSource。这样,当您从DataGridView中选择一个项目时,所有这些字段都将使用当前选择的DataGridView项目进行更新

假设数据库中有两个表的定义如下:

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

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar
Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)
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;
    }
}
    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;
        }
    }
    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;
        }
    }
假设数据库中有一个名为Citizen的表,定义如下:

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

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar
Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)
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;
    }
}
    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;
        }
    }
    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;
        }
    }
DataGridView绑定到BindingList,其中CitizenViewModel的定义如下:

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

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar
Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)
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;
    }
}
    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;
        }
    }
    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;
        }
    }
让我们将DGV的BindingSource命名为citizenViewModelBindingSource

表单上有两个组合框cmbCountry和cmbCity,它们分别绑定到Country和City类型的BindingSources,两个组合框的DisplayMember设置为Name,ValueMember设置为ID。cmbCountry的SelectedValue属性绑定到citizenViewModelBindingSource的CountryID属性,cmbCity的SelectedValue属性绑定到同一绑定源的CityID属性,因此组合框显示的值会根据DGV中选择的项目而变化

我正在处理countryBindingSource的CurrentChanged事件,该事件位于cmbCountry之后,因此当所选国家/地区发生更改时,cmbCity将显示该所选国家/地区的城市

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);
}
问题如下:假设我的结果集有5行,其中2行具有相同的CountryID但不同的CityID,另外3行具有所有不同的CountryID和CityID。当我选择两个具有相同CountryID的项目中的一个,然后选择另一个具有相同CountryID的项目时,带有cities的组合框将相应地更新。但是,如果我首先选择这两个具有相同CountryID的行中的一个,然后选择具有不同CountryID的行中的一个,并最终选择具有相同CountryID的另一行,则绑定源将神奇地将相同的CityID分配给先前选择的具有相同CountryID的行。令人困惑我将用一个例子来解释,并省略不相关的字段

结果: 1.姓名:约翰·多伊;国家:美国;;城市:西雅图 2.姓名:约翰·史密斯;国家:加拿大;城市:蒙特利尔 3.姓名:迈克尔欧文;国家:英国;;城市:利物浦 4.姓名:乔治·布什;国家:美国;;城市:华盛顿 5.姓名:弗拉基米尔·普京;国家:俄罗斯;城市:莫斯科

选择JohnDoe,组合框显示USA和西雅图。 选择乔治·布什,组合框显示美国和华盛顿。 选择JohnDoe,组合框显示USA和西雅图。 选择乔治·布什,组合框显示美国和华盛顿。所以一切都很好。 选择迈克尔欧文,组合框显示英格兰和利物浦。 选择JohnDoe,组合框显示USA和西雅图。 现在看这个。选择乔治·布什,组合框显示美国和西雅图,而不是美国和华盛顿。绑定源已经改变了乔治·布什的城市ID

关于这个问题我已经写完了,但还没有想到解决办法。为什么会发生这种情况以及如何避免这种行为

编辑


通过取消绑定cmbCity的SelectedValue并修改countryBindingSource\u CurrentChanged函数,解决了此问题,如下所示:

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

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar
Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)
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;
    }
}
    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;
        }
    }
    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;
        }
    }

但对我来说,这似乎是一个黑客行为,如果有人知道为什么会发生这种情况,我将保留这个问题。

通过解除cmbCity的SelectedValue的绑定并手动设置cmbCity.SelectedValue解决了这个问题。通过修改countryBindingSource\u CurrentChanged函数完成此操作,如下所示:

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

Table name: City
----------------
ID: integer, PK
CountryID: integer, FK to Country
Name: nvarchar
Table name: Citizen
-------------------
ID: integer, PK
CityID: integer, FK to City
Name: nvarchar
... (and other irrelevant fields)
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;
    }
}
    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;
        }
    }
    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;
        }
    }

我认为除了离开数据绑定之外,您别无选择,因为在组合框具有数据绑定时更改其内容会修改绑定到Selectedvalue的属性,不管您喜欢与否Gert Arnold

在VB中,当值真正更改时,组合框上会显示已更改的提交。如果出口在C,你试过了吗?数据库正在更新,或者只是UI错误?也许它是以伊特尔和阿辛顿的名字来选择第一个城市。@OwerFlov这只是一个例子,这两个城市的排序是巧合。DB没有得到更新,除了我发布的代码外,本例中没有涉及其他代码。有一个事件ListChanged,当我从DGV中选择一条记录时,它会以ItemChanged类型触发。您介意将您的解决方案移动到一个答案,并在适当的时候将其标记为已接受吗?我认为除了离开数据绑定之外,您不能做任何其他事情,因为在组合框具有数据绑定时更改其内容会修改属性绑定 选择值,无论您喜欢与否。