将不同的数据加载到同一组合框列的不同行中 C#
Load different data into different rows of the same comboboxcolumn c#
好的,我有一个带有两个组合框列的 dataGridView。
Bank 和 BankBranch,用户可以选择在组合框中添加多个银行,但分行列表取决于所选银行。
在第一行,这完美无缺。
在任何其他行上,当银行被选中时,所有行上的所有分行列都更新为该银行的分行列表。
我的问题是,我该如何做到这一点,以便在选择第二个或第三个银行时,只更新该行的分行列表,而不是所有其他行。
这就是我在玩的东西。
if (grid.CurrentCell != null)
{
if (grid.CurrentCell.ColumnIndex == 3)
{
if (grid.CurrentRow != null)
{
foreach (Bank bank in banks)
{
if (bank.Description.Trim() == grid.CurrentRow.Cells["gridBank"].Value.ToString().Trim())
{
bankID = bank.ID;
GetBankBranchList(grid.CurrentRow.Index);
}
}
}
}
}
这是 GetBankBranchList 方法
bankBranches = dal.GetByCriteria<BankBranch>(bankBranchQuery);
foreach (BankBranch bankBranch in bankBranches)
{
if (bankBranch.Active)
{
gridBranch.Items.Add(bankBranch.Description);
}
}
一个常见的误解是 DataGridViewComboBoxCell
的工作方式与常规 ComboBox
相同。在很多方面,确实如此。然而;常规 ComboBox
比 DataGridViewComboBoxCell
宽容得多。例如,在代码中,如果您尝试将常规 ComboBox
es 值设置为不在其项目列表中的值……那么……什么都不会发生。没有抛出 error/exception 并且根本没有设置值。 DataGridViewComboBoxCell
不是这种情况。将组合框单元格值设置为不在其项目列表中的值将导致 DataGridView
抛出其 DataError
异常。
您可以通过简单地 swallowing/ignoring 网格 DataError
来寻求最后的选择,但是,出于多种原因,这是一个糟糕的选择。特别是,在我们想要使用网格的组合框的情况下,您可能没有忽略网格 DataError
的奢侈,因为恒定的错误最终可能会压倒 UI.
一种在 DataGridView
中创建级联组合框的方法
正如其他人评论的那样,组合框列的一种可能解决方案是将每个组合框单元格包含不同的值...将组合框“列”数据源设置为包含“所有”可能的列表组合框值。然后,分别将每个组合框“单元格”DataSource
设置为组合框列 DataSource
的“filtered/subset”列表。这是下面完整示例中使用的方法。
我使用了您的 Bank-Branch 类型场景。具体来说,有 10 家不同的银行和 50 家不同的分行。每家银行可能有零个或多个分行。另外,一个Branch可能属于多个Bank,换句话说,很多银行可能有同一个Branch。
为了测试,我们需要一个客户。客户将有一个唯一的 ID、一个名称、一个 BankID
,可能还有一个 BranchID
。这将用于在设置网格的 DataSource
时测试组合框。但是,我们将把它分解为两个步骤。第一步是首先让两个组合框正常工作,不要设置网格数据源。第1步完成后,我们将进入第2步,处理设置网格数据源时的问题。
您可以继续创建一个新的 winforms 解决方案,将 DataGridView
和 Button
拖放到窗体上,将 Button
命名为 btnNewData
并连接它点击事件。在如下所示的步骤中复制下面的 posted 代码,以完成如下所示的表单……
加载数据时显示错误消息框。
正在显示过滤后的组合框。
步骤 1) 在不设置网格数据源的情况下设置组合框。
对于此示例,网格将设置为保存客户数据,组合框列将包含该客户的银行和分行值。
首先,我们将创建一个名为 SelectedCombo
的全局“常规”ComboBox
变量。当用户单击 Banks 组合框单元格时,网格 EditingControlShowing
事件将触发,我们会将 DataGridViewComboBoxCell
转换为“常规” ComboBox
,即 SelectedCombo
。然后,订阅 SelectedIndexChanged
事件。当 SelectedIndexChanged
事件触发时,我们将设置伴随的 Branch 单元格数据源。
我们将使用几个网格事件,下面是对这段代码中使用的事件的简要说明。
EditingControlShowing 事件…
当用户单击网格 cell/combo 框单元格并将该单元格置于“编辑”模式时触发。此事件在用户实际 types/changes 单元格中的任何内容之前触发。如果编辑的单元格是 Banks 组合框单元格,则代码设置全局 SelectedCombo
变量并订阅其 SelectedIndexChanged
事件。
CellLeave 事件…
当用户试图“离开”单元格时触发。此事件仅用于来自全局变量 SelectedCombo
Combo Boxes SelectedIndexChanged
事件的“un-subscribe”。否则,当用户单击 Branches 组合框单元格之一时,事件将不正确地触发。
DefaultValuesNeeded 事件…
当用户在网格的最后一个“新”行中输入内容或select组合框值时触发。如果组合框的数据源没有“empty/null”值,这可能会导致一些问题。因此,我们的想法是继续为组合框的“新”行提供一些默认值,即 Banks 列表中的第一个 Bank 和一个空的 Branch。
为了帮助我们创建三个简单的 Classes.
BranchCB Class
代表一个分支,有两个属性…… int
BranchID
和 string
BranchName
。并覆盖用于调试输出的 ToString()
方法。
此外,还有一个静态的BlankBranch
属性,即returns一个BranchCB
对象,BranchID
为0,空string
为 BranchName
。注意:使用 DataGridViewComboBoxCell
的一个可能问题是当单元格值变为 empty/null 时。格子马抱怨这个并扔掉它 DataError
。为了帮助最大程度地减少这种情况,并允许客户选择“否”分支机构,我们将向“分支机构”组合框列的数据源和每个银行的分支机构集合添加一个 BlankBranch
。即使银行“没有”分行,这个 BlankBranch
也会出现在银行的分行集合中。
public class BranchCB {
public int BranchID { get; set; }
public string BranchName { get; set; }
public static BranchCB BlankBranch {
get {
return new BranchCB { BranchID = 0, BranchName = "" };
}
}
public override string ToString() {
return "BranchID: " + BranchID + " Name: " + BranchName;
}
}
中央银行Class
BankCB
class是直截了当的,一个int
BankID
,一个string
BankName
和一个BindingList
BranchCB
个对象。用于调试的重写 ToString()
方法。
public class BankCB {
public int BankID { get; set; }
public string BankName { get; set; }
public BindingList<BranchCB> Branches { get; set; }
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.AppendLine("----------------------------------------------------------");
sb.AppendLine("BankID: " + BankID + " Name: " + BankName + " Branches:...");
if (Branches.Count > 1) {
foreach (BranchCB branch in Branches) {
if (branch.BranchID != 0) {
sb.AppendLine(branch.ToString());
}
}
}
else {
sb.AppendLine("No Branches");
}
return sb.ToString();
}
}
客户Class
A Customer
class 具有 int
CustomerID
、string
CustomerName
和两个 int
属性 BankID
和 BranchID
。 class 用于测试组合框。
public class Customer {
public int CustomerID { get; set; }
public string CustomerName { get; set; }
public int BankID { get; set; }
public int BranchID { get; set; }
}
对于这个例子,创建了五 (5) 个全局变量…
Random rand = new Random();
BindingList<BankCB> Banks;
BindingList<BranchCB> Branches;
BindingList<Customer> Customers;
ComboBox SelectedCombo;
rand
用于创建测试数据。
Banks
是 BankCB
对象的列表,将用作 DataDource
用于 Banks
DataGridViewComboBoxColumn
.
Branches
是所有 BranchCB
对象的列表,将用作 Branches
DataGridViewComboBoxColumn
.
的 DataSource
Customers
是 Customer
个对象的列表,将用作 DataGridView
.
的 DataSource
最后,SelectedCombo
是一个常规 ComboBox
并且如前所述使用。
正在创建一些测试随机银行和分行数据…
下面的代码创建并设置全局变量 Banks
和 Branches
变量,其中包含 50 个分行和 10 个银行。每家银行将有 0 到最多 10 个分行。一些分行可能会被遗漏,并且可能不会被任何银行使用。每家银行的分行列表不会包含重复的分行;但是,多个银行可能有相同的分行。
private void Setup_10_BanksWithRandomNumberOfBranches() {
Branches = new BindingList<BranchCB>();
Branches.Add(BranchCB.BlankBranch);
for (int numOfBranches = 1; numOfBranches <= 50; numOfBranches++) {
Branches.Add(new BranchCB { BranchID = numOfBranches, BranchName = "Branch " + numOfBranches });
}
Banks = new BindingList<BankCB>();
BindingList<BranchCB> tempBranches;
BranchCB curBranch;
int totBranches;
for (int numOfBank = 1; numOfBank <= 10; numOfBank++) {
tempBranches = new BindingList<BranchCB>();
tempBranches.Add(BranchCB.BlankBranch);
totBranches = rand.Next(0, 11);
for (int i = 0; i < totBranches; i++) {
curBranch = Branches[rand.Next(0, 50)];
if (!tempBranches.Contains(curBranch)) {
tempBranches.Add(curBranch);
}
}
tempBranches = new BindingList<BranchCB>(tempBranches.OrderBy(x => x.BranchID).ToList());
Banks.Add(new BankCB { BankID = numOfBank, BankName = "Bank " + numOfBank, Branches = tempBranches });
}
foreach (BankCB bank in Banks) {
Debug.WriteLine(bank);
}
}
将列添加到网格
设置网格 Banks 组合框列应该相当简单,因为所有组合框都包含相同的数据。我们要将 Bank 组合框列的 ValueMember
属性 设置为 BankID
并将其 DisplayMember
属性 设置为 BankName
。对于 Branches
组合框列,ValueMember
将是 BranchID
而 DisplayMember
将是 BranchName
.
private void AddColumns() {
dataGridView1.Columns.Add(GetTextBoxColumn("CustomerID", "Customer ID", "CustomerID"));
dataGridView1.Columns.Add(GetTextBoxColumn("CustomerName", "Customer Name", "CustomerName"));
DataGridViewComboBoxColumn col = GetComboBoxColumn("BankID", "BankName", "BankID", "Banks", "Banks");
col.DataSource = Banks;
dataGridView1.Columns.Add(col);
col = GetComboBoxColumn("BranchID", "BranchName", "BranchID", "Branches", "Branches");
col.DataSource = Branches;
dataGridView1.Columns.Add(col);
}
private DataGridViewComboBoxColumn GetComboBoxColumn(string dataPropertyName, string displayMember, string valueMember, string headerText, string name) {
DataGridViewComboBoxColumn cbCol = new DataGridViewComboBoxColumn();
cbCol.DataPropertyName = dataPropertyName;
cbCol.DisplayMember = displayMember;
cbCol.ValueMember = valueMember;
cbCol.HeaderText = headerText;
cbCol.Name = name;
return cbCol;
}
private DataGridViewTextBoxColumn GetTextBoxColumn(string dataPropertyName, string headerText, string name) {
DataGridViewTextBoxColumn txtCol = new DataGridViewTextBoxColumn();
txtCol.DataPropertyName = dataPropertyName;
txtCol.HeaderText = headerText;
txtCol.Name = name;
return txtCol;
}
此时,我们不想设置网格数据源,而是希望网格中的一行包含银行和分行组合框。如果我们 运行 来自表单加载事件的代码...
private void Form3_Load(object sender, EventArgs e) {
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
}
我们应该看到 Bank 和 Branch 组合框单元格,并且 selecting 银行组合框应该显示 10 个银行,而 Branches 组合框将显示所有 50 个分支机构加上“空白”分支机构。
过滤 Branches 组合框单元格
要订阅的第一个网格事件是网格 EditingControlShowing
事件。如果编辑的单元格“是”一个 Bank 组合框单元格,那么,我们要将该 Bank 组合框单元格转换为我们的全局 SelectedCombo
ComboBox
变量。然后让全局 SelectedCombo
订阅 is SelectedIndexChanged
事件。
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
if (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name == "Banks") {
SelectedCombo = e.Control as ComboBox;
if (SelectedCombo != null) {
SelectedCombo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
SelectedCombo.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);
}
}
}
我们需要实现ComboBox_SelectedIndexChanged
事件。当这个事件触发时,我们知道 Bank selection 已经改变,我们想要设置伴随的 Branch cells 数据源。我们可以从全局 SelectedCombo.SelectedItem
属性 中获取 selected BankCB
对象。接下来,我们获取该行的 Branches 组合框单元格,并将 DataSource
设置为 selected BankCB
的 Branches
集合。最后,将 Branches 单元格的 Value
设置为“空白”分支,这将始终是列表中的第一个“空”分支。
即使我们将单元格数据源更改为不同的列表,我们也可以确信,每个银行的分行列表都是分行组合框列 DataSource
中使用的所有分行的“子集” .这应该有助于最大限度地减少抛出网格 DataError
.
的机会
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (SelectedCombo.SelectedValue != null) {
BankCB selectedBank = (BankCB)SelectedCombo.SelectedItem;
DataGridViewComboBoxCell branchCell = (DataGridViewComboBoxCell)(dataGridView1.CurrentRow.Cells["Branches"]);
branchCell.DataSource = selectedBank.Branches;
branchCell.Value = selectedBank.Branches[0].BranchID;
}
}
如果我们现在 运行 代码……您应该注意……在您单击银行组合框单元格之前……如果您单击分支组合框单元格,那么您将看到“所有”分支机构.但是,如果您 select/change Bank 组合框值,然后单击 Branches 组合框单元格……您将在我们的 ComboBox_SelectedIndexChanges
代码中遇到转换错误。
问题是全局 ComboBox
es SelectedCombo
的 SelectedIndexChanged
事件仍然连接并且会在 selected 分支组合框时触发...我们不想要的。当用户“离开”银行单元格时,我们需要从其 SelectedIndexChanged
事件中 UN-subscribe SelectedCombo
。因此,从全局 SelectedCombo_SelectedIndexChanged
事件连接网格 CellLeave
事件和 UN-subscribing 应该可以解决这个问题。
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e) {
if (dataGridView1.Columns[e.ColumnIndex].Name == "Banks") {
SelectedCombo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
}
}
如果我们现在 运行 代码……用户可以无错误地更改 Branch 组合框。但是,可能存在一个问题……“在”用户 select 成为银行之前,用户可以单击 Branches 组合框,由于尚未设置 Bank 组合框,用户将看到“所有”分支并且可以 select 任何分支。这可能会使 Branch 处于不一致状态,其中 Branch selected 但没有 Bank selected。我们不希望这种情况发生。
在这次考试中例如,我们将连接网格 DefaultValuesNeeded
事件,通过将 BankID 设置为银行列表中的第一家银行,将新行设置为“默认”状态。并将新行的分支值设置为“空白”分支。这应该注意防止用户在没有“首先”select银行的情况下select银行。
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) {
int newCustID = 1;
if (Customers != null) {
newCustID = Customers.Count;
}
e.Row.Cells["CustomerID"].Value = newCustID;
DataGridViewComboBoxCell cbCell = (DataGridViewComboBoxCell)e.Row.Cells["Banks"];
cbCell.DataSource = Banks;
cbCell.Value = Banks[0].BankID;
cbCell = (DataGridViewComboBoxCell)e.Row.Cells["Branches"];
cbCell.DataSource = Banks[0].Branches;
cbCell.Value = Banks[0].Branches[0].BranchID;
}
现在组合框应该可以正常工作,不会出现错误。如果用户添加行,上述方法将有助于避免不一致的 Bank-Branch 状态。这应该结束第一步。
步骤 2) 添加一个 DatSource
到网格。
我们需要一些测试 Customer
数据。对于此测试,Customer
数据将为 18 Customers
。前 15 个将具有有效的 Bank 和 Branch 值。 “客户 16”将有一个无效的银行号码,17 个将有一个无效的分行,最后 18 个将有一个有效的银行和一个有效的分行但是,分行值将不在客户的分行集合中 selected银行.
private BindingList<Customer> GetCustomers() {
BindingList<Customer> customers = new BindingList<Customer>();
BankCB curBank;
BranchCB curBranchID;
for (int i = 1; i <= 15; i++) {
curBank = Banks[rand.Next(0, Banks.Count)];
if (curBank.Branches.Count > 0) {
curBranchID = curBank.Branches[rand.Next(0, curBank.Branches.Count)];
customers.Add(new Customer { CustomerID = i, CustomerName = "Cust " + i, BankID = curBank.BankID, BranchID = curBranchID.BranchID });
}
else {
customers.Add(new Customer { CustomerID = i, CustomerName = "Cust " + i, BankID = curBank.BankID, BranchID = BranchCB.BlankBranch.BranchID });
}
}
customers.Add(new Customer { CustomerID = 16, CustomerName = "Bad Cust 16", BankID = 22, BranchID = 1 });
customers.Add(new Customer { CustomerID = 17, CustomerName = "Bad Cust 17", BankID = 3, BranchID = 55 });
customers.Add(new Customer { CustomerID = 18, CustomerName = "Bad Cust 18", BankID = 3, BranchID = 1 });
return customers;
}
更新表单加载事件可能类似于...
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);Setup_10_BanksWithRandomNumberOfBranches();
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
Customers = GetCustomers();
dataGridView1.DataSource = Customers;
}
如果您 运行 这段代码,对于每个不良客户测试数据,您应该至少两次获得网格的 DataError
关注。设置网格数据源后,您可能会看到两个坏客户(16 和 17),这样 Bank 或 Branch 组合框的值为空。如果将光标滚动到这两个组合框上,您应该会看到不断触发数据错误,基本上我们需要解决这个问题。此外,如果您查看坏客户 18,您会注意到 Branch 组合框值设置为不一致状态...在我的特定测试中...Branch 1 不是 Bank 3 中的 Branch。
错误的原因很明显,但是解决方法却不多。在这种情况下,我们有一个 DataSource
到网格,其中包含组合框的错误数据,不幸的是,我们不能忽略它。我们必须做点什么。在这种情况下,没有“好的”选项,数据库中的数据显然已损坏,我们不能不采取措施继续。所以……,您可以通过删除该行来将其排除在外……或者……您可以将错误值添加为新的组合框值……或者……您可以将错误值“更改”为 default/good 值。其他选项可能适用,但底线是……我们想继续,但我们必须先对不良价值观采取措施。所以……自己挑毒。
在这个例子中,我将使用最后一个选项,并将错误值更改为 default/valid 值,并向用户弹出一个消息框,让他们知道发生了什么变化,然后继续。
在设置网格 DataSource
之前,我们必须检查客户数据中的组合框值。因此,创建了一个循环遍历 Customers
列表并检查错误的 Bank 和错误的 Branch 值的小方法。如果发现错误的 Bank 值,则 Bank 值将设置为 Banks 列表中的第一个 Bank。如果 Bank 值没问题,但 Branch 不好,那么我们将 Branch 设置为“空白”Branch。
这看起来有很多代码;但是,大部分代码都在构建错误字符串。您可以简单地更改值并继续并且永远不会打扰用户。但是,至少,调试或日志语句对于调试目的来说是个好主意。
首先检查 BankID
是否有效,然后检查 BranchID
。如果其中一个是错误的,那么错误的值将被替换为 default/valid 值。
请记住……由于每个 Branch 组合框单元格的项目列表是基于 selected 的 Bank,因此,我们需要查看客户的 selected Bank's Branches 集合和查看 Customers BranchID
是否是该银行的分支机构集合中的分支机构之一。
private void CheckDataForBadComboBoxValues() {
StringBuilder sb = new StringBuilder();
foreach (Customer cust in Customers) {
sb.Clear();
List<BankCB> targetBank = Banks.Where(x => x.BankID == cust.BankID).ToList();
if (targetBank.Count > 0) {
BankCB curBank = targetBank[0];
var targetBranch = curBank.Branches.Where(x => x.BranchID == cust.BranchID).ToList();
if (targetBranch.Count > 0) {
sb.AppendLine("Valid bank and branch");
Debug.Write(sb.ToString());
}
else {
sb.AppendLine("Invalid Branch ID ----");
sb.AppendLine("CutomerID: " + cust.CustomerID + " Name: " + cust.CustomerName);
sb.AppendLine("BankID: " + cust.BankID + " BranchID: " + cust.BranchID);
sb.AppendLine("Setting Bank to : " + cust.BankID + " setting branch to empty branch");
MessageBox.Show(sb.ToString(), "Invalid Branch ID!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Debug.WriteLine(sb.ToString());
if (curBank.Branches.Count > 0) {
cust.BranchID = curBank.Branches[0].BranchID;
}
}
}
else {
sb.AppendLine("Invalid Bank ID ----");
sb.AppendLine("CutomerID: " + cust.CustomerID + " Name: " + cust.CustomerName);
sb.AppendLine("BankID: " + cust.BankID + " BranchID: " + cust.BranchID);
sb.AppendLine("Setting Bank to first bank, setting branch to empty branch");
MessageBox.Show(sb.ToString(), "Invalid Bank ID!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Debug.WriteLine(sb.ToString());
cust.BankID = Banks[0].BankID;
if (Banks[0].Branches.Count > 0) {
cust.BranchID = Banks[0].Branches[0].BranchID;
}
else {
cust.BranchID = BranchCB.BlankBranch.BranchID;
}
}
}
}
在设置网格之前调用此方法 DataSource
应该会消除之前的 DataError
。我强烈建议在将其设置为 DataSource
之前检查网格 DataSource
值。具体来说,组合框的值只是为了避免可能的代码崩溃。我不相信“好的”数据,所以只需要对 CYA 进行检查。
现在更新的表单 Load
方法可能类似于...
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
Customers = GetCustomers();
CheckDataForBadComboBoxValues();
dataGridView1.DataSource = Customers;
}
这应该在设置网格数据源时摆脱网格的 DataError
。此外,Customer 18 的 bad Branch 值设置为我们想要的空白 Branch。然而,还有一个小问题……当设置网格数据源时,之前actions/work我们通过使用网格的事件来管理每个Bank-Branch组合框单元格……当网格时没有发生DataSource
已设置。当每个 Customer 行都添加到网格中时,我们之前用于设置每个 Branch 组合框单元格的事件 DataSource
没有触发。
Branch 组合框将在组合框中显示正确的 selected Customer's Branch 值,但是,如果单击 Branch 组合框,您将看到自单个 Branch 组合框以来的所有分支单元格 DataSource
尚未设置。
所以,我们ned 另一种方法循环遍历网格的行集合,获取行 selected Bank,然后将 Branch 组合框单元格的 DataSource
设置为 selected Banks Branches 集合。我们只需要在设置网格数据源后执行一次。
private void SetAllBranchComboCellsDataSource() {
Customer curCust;
foreach (DataGridViewRow row in dataGridView1.Rows) {
if (!row.IsNewRow) {
curCust = (Customer)row.DataBoundItem;
BankCB bank = (BankCB)Banks.Where(x => x.BankID == curCust.BankID).FirstOrDefault();
// since we already checked for valid Bank values, we know the bank id is a valid bank id
DataGridViewComboBoxCell cbCell = (DataGridViewComboBoxCell)row.Cells["Branches"];
cbCell.DataSource = bank.Branches;
}
}
}
此更改后,网格最终更新 Load
方法可能如下所示。将网格 EditMode
设置为 EditOnEnter
将有助于单击组合框单元格一次以显示下拉列表。另外一个Button
被添加到表单到re-set用于测试的网格数据。
Random rand = new Random();
BindingList<BankCB> Banks;
BindingList<BranchCB> Branches;
BindingList<Customer> Customers;
ComboBox SelectedCombo;
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
SetNewData();
}
private void SetNewData() {
dataGridView1.Columns.Clear();
Setup_10_BanksWithRandomNumberOfBranches();
//Setup_10_BanksWith5BranchesNoDuplicates();
AddColumns();
Customers = GetCustomers();
CheckDataForBadComboBoxValues();
dataGridView1.DataSource = Customers;
SetAllBranchComboCellsDataSource();
}
private void btnNewData_Click(object sender, EventArgs e) {
SetNewData();
}
这应该完成了这个例子。然而,当分支数量与随机分支 selected 一起随机生成时,测试这方面的某些方面可能是一个挑战。换句话说,每次执行代码都会产生不同的数据。为了消除这种随机性,我创建了第二组数据,其中有 10 家银行和 50 家分行。每家银行恰好有五 (5) 家分行。此外,每个分行属于一家且仅一家银行。银行 1 有分行 1-5; Bank 2 有 Branch 6-10 等,所有 50 个 Branch 只使用一次。对于测试,使用此数据可能更容易。
调用此方法而不是 Setup_10_BanksWithRandomNumberOfBranches();
private void Setup_10_BanksWith5BranchesNoDuplicates() {
Branches = new BindingList<BranchCB>();
Branches.Add(BranchCB.BlankBranch);
for (int numOfBranches = 1; numOfBranches <= 50; numOfBranches++) {
Branches.Add(new BranchCB { BranchID = numOfBranches, BranchName = "Branch " + numOfBranches });
}
Banks = new BindingList<BankCB>();
BindingList<BranchCB> tempBranches;
BranchCB curBranch;
int branchIndex = 1;
for (int numOfBank = 1; numOfBank <= 10; numOfBank++) {
tempBranches = new BindingList<BranchCB>();
tempBranches.Add(BranchCB.BlankBranch);
for (int i = 0; i < 5; i++) {
if (branchIndex < Branches.Count) {
curBranch = Branches[branchIndex++];
tempBranches.Add(curBranch);
}
else {
break;
}
}
tempBranches = new BindingList<BranchCB>(tempBranches.OrderBy(x => x.BranchID).ToList());
Banks.Add(new BankCB { BankID = numOfBank, BankName = "Bank " + numOfBank, Branches = tempBranches });
}
}
很抱歉 post。
我希望这有意义并且有所帮助。
好的,我有一个带有两个组合框列的 dataGridView。
Bank 和 BankBranch,用户可以选择在组合框中添加多个银行,但分行列表取决于所选银行。
在第一行,这完美无缺。
在任何其他行上,当银行被选中时,所有行上的所有分行列都更新为该银行的分行列表。
我的问题是,我该如何做到这一点,以便在选择第二个或第三个银行时,只更新该行的分行列表,而不是所有其他行。
这就是我在玩的东西。
if (grid.CurrentCell != null)
{
if (grid.CurrentCell.ColumnIndex == 3)
{
if (grid.CurrentRow != null)
{
foreach (Bank bank in banks)
{
if (bank.Description.Trim() == grid.CurrentRow.Cells["gridBank"].Value.ToString().Trim())
{
bankID = bank.ID;
GetBankBranchList(grid.CurrentRow.Index);
}
}
}
}
}
这是 GetBankBranchList 方法
bankBranches = dal.GetByCriteria<BankBranch>(bankBranchQuery);
foreach (BankBranch bankBranch in bankBranches)
{
if (bankBranch.Active)
{
gridBranch.Items.Add(bankBranch.Description);
}
}
一个常见的误解是 DataGridViewComboBoxCell
的工作方式与常规 ComboBox
相同。在很多方面,确实如此。然而;常规 ComboBox
比 DataGridViewComboBoxCell
宽容得多。例如,在代码中,如果您尝试将常规 ComboBox
es 值设置为不在其项目列表中的值……那么……什么都不会发生。没有抛出 error/exception 并且根本没有设置值。 DataGridViewComboBoxCell
不是这种情况。将组合框单元格值设置为不在其项目列表中的值将导致 DataGridView
抛出其 DataError
异常。
您可以通过简单地 swallowing/ignoring 网格 DataError
来寻求最后的选择,但是,出于多种原因,这是一个糟糕的选择。特别是,在我们想要使用网格的组合框的情况下,您可能没有忽略网格 DataError
的奢侈,因为恒定的错误最终可能会压倒 UI.
一种在 DataGridView
正如其他人评论的那样,组合框列的一种可能解决方案是将每个组合框单元格包含不同的值...将组合框“列”数据源设置为包含“所有”可能的列表组合框值。然后,分别将每个组合框“单元格”DataSource
设置为组合框列 DataSource
的“filtered/subset”列表。这是下面完整示例中使用的方法。
我使用了您的 Bank-Branch 类型场景。具体来说,有 10 家不同的银行和 50 家不同的分行。每家银行可能有零个或多个分行。另外,一个Branch可能属于多个Bank,换句话说,很多银行可能有同一个Branch。
为了测试,我们需要一个客户。客户将有一个唯一的 ID、一个名称、一个 BankID
,可能还有一个 BranchID
。这将用于在设置网格的 DataSource
时测试组合框。但是,我们将把它分解为两个步骤。第一步是首先让两个组合框正常工作,不要设置网格数据源。第1步完成后,我们将进入第2步,处理设置网格数据源时的问题。
您可以继续创建一个新的 winforms 解决方案,将 DataGridView
和 Button
拖放到窗体上,将 Button
命名为 btnNewData
并连接它点击事件。在如下所示的步骤中复制下面的 posted 代码,以完成如下所示的表单……
加载数据时显示错误消息框。
正在显示过滤后的组合框。
步骤 1) 在不设置网格数据源的情况下设置组合框。
对于此示例,网格将设置为保存客户数据,组合框列将包含该客户的银行和分行值。
首先,我们将创建一个名为 SelectedCombo
的全局“常规”ComboBox
变量。当用户单击 Banks 组合框单元格时,网格 EditingControlShowing
事件将触发,我们会将 DataGridViewComboBoxCell
转换为“常规” ComboBox
,即 SelectedCombo
。然后,订阅 SelectedIndexChanged
事件。当 SelectedIndexChanged
事件触发时,我们将设置伴随的 Branch 单元格数据源。
我们将使用几个网格事件,下面是对这段代码中使用的事件的简要说明。
EditingControlShowing 事件…
当用户单击网格 cell/combo 框单元格并将该单元格置于“编辑”模式时触发。此事件在用户实际 types/changes 单元格中的任何内容之前触发。如果编辑的单元格是 Banks 组合框单元格,则代码设置全局 SelectedCombo
变量并订阅其 SelectedIndexChanged
事件。
CellLeave 事件…
当用户试图“离开”单元格时触发。此事件仅用于来自全局变量 SelectedCombo
Combo Boxes SelectedIndexChanged
事件的“un-subscribe”。否则,当用户单击 Branches 组合框单元格之一时,事件将不正确地触发。
DefaultValuesNeeded 事件…
当用户在网格的最后一个“新”行中输入内容或select组合框值时触发。如果组合框的数据源没有“empty/null”值,这可能会导致一些问题。因此,我们的想法是继续为组合框的“新”行提供一些默认值,即 Banks 列表中的第一个 Bank 和一个空的 Branch。
为了帮助我们创建三个简单的 Classes.
BranchCB Class
代表一个分支,有两个属性…… int
BranchID
和 string
BranchName
。并覆盖用于调试输出的 ToString()
方法。
此外,还有一个静态的BlankBranch
属性,即returns一个BranchCB
对象,BranchID
为0,空string
为 BranchName
。注意:使用 DataGridViewComboBoxCell
的一个可能问题是当单元格值变为 empty/null 时。格子马抱怨这个并扔掉它 DataError
。为了帮助最大程度地减少这种情况,并允许客户选择“否”分支机构,我们将向“分支机构”组合框列的数据源和每个银行的分支机构集合添加一个 BlankBranch
。即使银行“没有”分行,这个 BlankBranch
也会出现在银行的分行集合中。
public class BranchCB {
public int BranchID { get; set; }
public string BranchName { get; set; }
public static BranchCB BlankBranch {
get {
return new BranchCB { BranchID = 0, BranchName = "" };
}
}
public override string ToString() {
return "BranchID: " + BranchID + " Name: " + BranchName;
}
}
中央银行Class
BankCB
class是直截了当的,一个int
BankID
,一个string
BankName
和一个BindingList
BranchCB
个对象。用于调试的重写 ToString()
方法。
public class BankCB {
public int BankID { get; set; }
public string BankName { get; set; }
public BindingList<BranchCB> Branches { get; set; }
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.AppendLine("----------------------------------------------------------");
sb.AppendLine("BankID: " + BankID + " Name: " + BankName + " Branches:...");
if (Branches.Count > 1) {
foreach (BranchCB branch in Branches) {
if (branch.BranchID != 0) {
sb.AppendLine(branch.ToString());
}
}
}
else {
sb.AppendLine("No Branches");
}
return sb.ToString();
}
}
客户Class
A Customer
class 具有 int
CustomerID
、string
CustomerName
和两个 int
属性 BankID
和 BranchID
。 class 用于测试组合框。
public class Customer {
public int CustomerID { get; set; }
public string CustomerName { get; set; }
public int BankID { get; set; }
public int BranchID { get; set; }
}
对于这个例子,创建了五 (5) 个全局变量…
Random rand = new Random();
BindingList<BankCB> Banks;
BindingList<BranchCB> Branches;
BindingList<Customer> Customers;
ComboBox SelectedCombo;
rand
用于创建测试数据。
Banks
是 BankCB
对象的列表,将用作 DataDource
用于 Banks
DataGridViewComboBoxColumn
.
Branches
是所有 BranchCB
对象的列表,将用作 Branches
DataGridViewComboBoxColumn
.
DataSource
Customers
是 Customer
个对象的列表,将用作 DataGridView
.
DataSource
最后,SelectedCombo
是一个常规 ComboBox
并且如前所述使用。
正在创建一些测试随机银行和分行数据…
下面的代码创建并设置全局变量 Banks
和 Branches
变量,其中包含 50 个分行和 10 个银行。每家银行将有 0 到最多 10 个分行。一些分行可能会被遗漏,并且可能不会被任何银行使用。每家银行的分行列表不会包含重复的分行;但是,多个银行可能有相同的分行。
private void Setup_10_BanksWithRandomNumberOfBranches() {
Branches = new BindingList<BranchCB>();
Branches.Add(BranchCB.BlankBranch);
for (int numOfBranches = 1; numOfBranches <= 50; numOfBranches++) {
Branches.Add(new BranchCB { BranchID = numOfBranches, BranchName = "Branch " + numOfBranches });
}
Banks = new BindingList<BankCB>();
BindingList<BranchCB> tempBranches;
BranchCB curBranch;
int totBranches;
for (int numOfBank = 1; numOfBank <= 10; numOfBank++) {
tempBranches = new BindingList<BranchCB>();
tempBranches.Add(BranchCB.BlankBranch);
totBranches = rand.Next(0, 11);
for (int i = 0; i < totBranches; i++) {
curBranch = Branches[rand.Next(0, 50)];
if (!tempBranches.Contains(curBranch)) {
tempBranches.Add(curBranch);
}
}
tempBranches = new BindingList<BranchCB>(tempBranches.OrderBy(x => x.BranchID).ToList());
Banks.Add(new BankCB { BankID = numOfBank, BankName = "Bank " + numOfBank, Branches = tempBranches });
}
foreach (BankCB bank in Banks) {
Debug.WriteLine(bank);
}
}
将列添加到网格
设置网格 Banks 组合框列应该相当简单,因为所有组合框都包含相同的数据。我们要将 Bank 组合框列的 ValueMember
属性 设置为 BankID
并将其 DisplayMember
属性 设置为 BankName
。对于 Branches
组合框列,ValueMember
将是 BranchID
而 DisplayMember
将是 BranchName
.
private void AddColumns() {
dataGridView1.Columns.Add(GetTextBoxColumn("CustomerID", "Customer ID", "CustomerID"));
dataGridView1.Columns.Add(GetTextBoxColumn("CustomerName", "Customer Name", "CustomerName"));
DataGridViewComboBoxColumn col = GetComboBoxColumn("BankID", "BankName", "BankID", "Banks", "Banks");
col.DataSource = Banks;
dataGridView1.Columns.Add(col);
col = GetComboBoxColumn("BranchID", "BranchName", "BranchID", "Branches", "Branches");
col.DataSource = Branches;
dataGridView1.Columns.Add(col);
}
private DataGridViewComboBoxColumn GetComboBoxColumn(string dataPropertyName, string displayMember, string valueMember, string headerText, string name) {
DataGridViewComboBoxColumn cbCol = new DataGridViewComboBoxColumn();
cbCol.DataPropertyName = dataPropertyName;
cbCol.DisplayMember = displayMember;
cbCol.ValueMember = valueMember;
cbCol.HeaderText = headerText;
cbCol.Name = name;
return cbCol;
}
private DataGridViewTextBoxColumn GetTextBoxColumn(string dataPropertyName, string headerText, string name) {
DataGridViewTextBoxColumn txtCol = new DataGridViewTextBoxColumn();
txtCol.DataPropertyName = dataPropertyName;
txtCol.HeaderText = headerText;
txtCol.Name = name;
return txtCol;
}
此时,我们不想设置网格数据源,而是希望网格中的一行包含银行和分行组合框。如果我们 运行 来自表单加载事件的代码...
private void Form3_Load(object sender, EventArgs e) {
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
}
我们应该看到 Bank 和 Branch 组合框单元格,并且 selecting 银行组合框应该显示 10 个银行,而 Branches 组合框将显示所有 50 个分支机构加上“空白”分支机构。
过滤 Branches 组合框单元格
要订阅的第一个网格事件是网格 EditingControlShowing
事件。如果编辑的单元格“是”一个 Bank 组合框单元格,那么,我们要将该 Bank 组合框单元格转换为我们的全局 SelectedCombo
ComboBox
变量。然后让全局 SelectedCombo
订阅 is SelectedIndexChanged
事件。
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
if (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name == "Banks") {
SelectedCombo = e.Control as ComboBox;
if (SelectedCombo != null) {
SelectedCombo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
SelectedCombo.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);
}
}
}
我们需要实现ComboBox_SelectedIndexChanged
事件。当这个事件触发时,我们知道 Bank selection 已经改变,我们想要设置伴随的 Branch cells 数据源。我们可以从全局 SelectedCombo.SelectedItem
属性 中获取 selected BankCB
对象。接下来,我们获取该行的 Branches 组合框单元格,并将 DataSource
设置为 selected BankCB
的 Branches
集合。最后,将 Branches 单元格的 Value
设置为“空白”分支,这将始终是列表中的第一个“空”分支。
即使我们将单元格数据源更改为不同的列表,我们也可以确信,每个银行的分行列表都是分行组合框列 DataSource
中使用的所有分行的“子集” .这应该有助于最大限度地减少抛出网格 DataError
.
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (SelectedCombo.SelectedValue != null) {
BankCB selectedBank = (BankCB)SelectedCombo.SelectedItem;
DataGridViewComboBoxCell branchCell = (DataGridViewComboBoxCell)(dataGridView1.CurrentRow.Cells["Branches"]);
branchCell.DataSource = selectedBank.Branches;
branchCell.Value = selectedBank.Branches[0].BranchID;
}
}
如果我们现在 运行 代码……您应该注意……在您单击银行组合框单元格之前……如果您单击分支组合框单元格,那么您将看到“所有”分支机构.但是,如果您 select/change Bank 组合框值,然后单击 Branches 组合框单元格……您将在我们的 ComboBox_SelectedIndexChanges
代码中遇到转换错误。
问题是全局 ComboBox
es SelectedCombo
的 SelectedIndexChanged
事件仍然连接并且会在 selected 分支组合框时触发...我们不想要的。当用户“离开”银行单元格时,我们需要从其 SelectedIndexChanged
事件中 UN-subscribe SelectedCombo
。因此,从全局 SelectedCombo_SelectedIndexChanged
事件连接网格 CellLeave
事件和 UN-subscribing 应该可以解决这个问题。
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e) {
if (dataGridView1.Columns[e.ColumnIndex].Name == "Banks") {
SelectedCombo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
}
}
如果我们现在 运行 代码……用户可以无错误地更改 Branch 组合框。但是,可能存在一个问题……“在”用户 select 成为银行之前,用户可以单击 Branches 组合框,由于尚未设置 Bank 组合框,用户将看到“所有”分支并且可以 select 任何分支。这可能会使 Branch 处于不一致状态,其中 Branch selected 但没有 Bank selected。我们不希望这种情况发生。
在这次考试中例如,我们将连接网格 DefaultValuesNeeded
事件,通过将 BankID 设置为银行列表中的第一家银行,将新行设置为“默认”状态。并将新行的分支值设置为“空白”分支。这应该注意防止用户在没有“首先”select银行的情况下select银行。
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e) {
int newCustID = 1;
if (Customers != null) {
newCustID = Customers.Count;
}
e.Row.Cells["CustomerID"].Value = newCustID;
DataGridViewComboBoxCell cbCell = (DataGridViewComboBoxCell)e.Row.Cells["Banks"];
cbCell.DataSource = Banks;
cbCell.Value = Banks[0].BankID;
cbCell = (DataGridViewComboBoxCell)e.Row.Cells["Branches"];
cbCell.DataSource = Banks[0].Branches;
cbCell.Value = Banks[0].Branches[0].BranchID;
}
现在组合框应该可以正常工作,不会出现错误。如果用户添加行,上述方法将有助于避免不一致的 Bank-Branch 状态。这应该结束第一步。
步骤 2) 添加一个 DatSource
到网格。
我们需要一些测试 Customer
数据。对于此测试,Customer
数据将为 18 Customers
。前 15 个将具有有效的 Bank 和 Branch 值。 “客户 16”将有一个无效的银行号码,17 个将有一个无效的分行,最后 18 个将有一个有效的银行和一个有效的分行但是,分行值将不在客户的分行集合中 selected银行.
private BindingList<Customer> GetCustomers() {
BindingList<Customer> customers = new BindingList<Customer>();
BankCB curBank;
BranchCB curBranchID;
for (int i = 1; i <= 15; i++) {
curBank = Banks[rand.Next(0, Banks.Count)];
if (curBank.Branches.Count > 0) {
curBranchID = curBank.Branches[rand.Next(0, curBank.Branches.Count)];
customers.Add(new Customer { CustomerID = i, CustomerName = "Cust " + i, BankID = curBank.BankID, BranchID = curBranchID.BranchID });
}
else {
customers.Add(new Customer { CustomerID = i, CustomerName = "Cust " + i, BankID = curBank.BankID, BranchID = BranchCB.BlankBranch.BranchID });
}
}
customers.Add(new Customer { CustomerID = 16, CustomerName = "Bad Cust 16", BankID = 22, BranchID = 1 });
customers.Add(new Customer { CustomerID = 17, CustomerName = "Bad Cust 17", BankID = 3, BranchID = 55 });
customers.Add(new Customer { CustomerID = 18, CustomerName = "Bad Cust 18", BankID = 3, BranchID = 1 });
return customers;
}
更新表单加载事件可能类似于...
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);Setup_10_BanksWithRandomNumberOfBranches();
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
Customers = GetCustomers();
dataGridView1.DataSource = Customers;
}
如果您 运行 这段代码,对于每个不良客户测试数据,您应该至少两次获得网格的 DataError
关注。设置网格数据源后,您可能会看到两个坏客户(16 和 17),这样 Bank 或 Branch 组合框的值为空。如果将光标滚动到这两个组合框上,您应该会看到不断触发数据错误,基本上我们需要解决这个问题。此外,如果您查看坏客户 18,您会注意到 Branch 组合框值设置为不一致状态...在我的特定测试中...Branch 1 不是 Bank 3 中的 Branch。
错误的原因很明显,但是解决方法却不多。在这种情况下,我们有一个 DataSource
到网格,其中包含组合框的错误数据,不幸的是,我们不能忽略它。我们必须做点什么。在这种情况下,没有“好的”选项,数据库中的数据显然已损坏,我们不能不采取措施继续。所以……,您可以通过删除该行来将其排除在外……或者……您可以将错误值添加为新的组合框值……或者……您可以将错误值“更改”为 default/good 值。其他选项可能适用,但底线是……我们想继续,但我们必须先对不良价值观采取措施。所以……自己挑毒。
在这个例子中,我将使用最后一个选项,并将错误值更改为 default/valid 值,并向用户弹出一个消息框,让他们知道发生了什么变化,然后继续。
在设置网格 DataSource
之前,我们必须检查客户数据中的组合框值。因此,创建了一个循环遍历 Customers
列表并检查错误的 Bank 和错误的 Branch 值的小方法。如果发现错误的 Bank 值,则 Bank 值将设置为 Banks 列表中的第一个 Bank。如果 Bank 值没问题,但 Branch 不好,那么我们将 Branch 设置为“空白”Branch。
这看起来有很多代码;但是,大部分代码都在构建错误字符串。您可以简单地更改值并继续并且永远不会打扰用户。但是,至少,调试或日志语句对于调试目的来说是个好主意。
首先检查 BankID
是否有效,然后检查 BranchID
。如果其中一个是错误的,那么错误的值将被替换为 default/valid 值。
请记住……由于每个 Branch 组合框单元格的项目列表是基于 selected 的 Bank,因此,我们需要查看客户的 selected Bank's Branches 集合和查看 Customers BranchID
是否是该银行的分支机构集合中的分支机构之一。
private void CheckDataForBadComboBoxValues() {
StringBuilder sb = new StringBuilder();
foreach (Customer cust in Customers) {
sb.Clear();
List<BankCB> targetBank = Banks.Where(x => x.BankID == cust.BankID).ToList();
if (targetBank.Count > 0) {
BankCB curBank = targetBank[0];
var targetBranch = curBank.Branches.Where(x => x.BranchID == cust.BranchID).ToList();
if (targetBranch.Count > 0) {
sb.AppendLine("Valid bank and branch");
Debug.Write(sb.ToString());
}
else {
sb.AppendLine("Invalid Branch ID ----");
sb.AppendLine("CutomerID: " + cust.CustomerID + " Name: " + cust.CustomerName);
sb.AppendLine("BankID: " + cust.BankID + " BranchID: " + cust.BranchID);
sb.AppendLine("Setting Bank to : " + cust.BankID + " setting branch to empty branch");
MessageBox.Show(sb.ToString(), "Invalid Branch ID!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Debug.WriteLine(sb.ToString());
if (curBank.Branches.Count > 0) {
cust.BranchID = curBank.Branches[0].BranchID;
}
}
}
else {
sb.AppendLine("Invalid Bank ID ----");
sb.AppendLine("CutomerID: " + cust.CustomerID + " Name: " + cust.CustomerName);
sb.AppendLine("BankID: " + cust.BankID + " BranchID: " + cust.BranchID);
sb.AppendLine("Setting Bank to first bank, setting branch to empty branch");
MessageBox.Show(sb.ToString(), "Invalid Bank ID!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Debug.WriteLine(sb.ToString());
cust.BankID = Banks[0].BankID;
if (Banks[0].Branches.Count > 0) {
cust.BranchID = Banks[0].Branches[0].BranchID;
}
else {
cust.BranchID = BranchCB.BlankBranch.BranchID;
}
}
}
}
在设置网格之前调用此方法 DataSource
应该会消除之前的 DataError
。我强烈建议在将其设置为 DataSource
之前检查网格 DataSource
值。具体来说,组合框的值只是为了避免可能的代码崩溃。我不相信“好的”数据,所以只需要对 CYA 进行检查。
现在更新的表单 Load
方法可能类似于...
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
Setup_10_BanksWithRandomNumberOfBranches();
AddColumns();
Customers = GetCustomers();
CheckDataForBadComboBoxValues();
dataGridView1.DataSource = Customers;
}
这应该在设置网格数据源时摆脱网格的 DataError
。此外,Customer 18 的 bad Branch 值设置为我们想要的空白 Branch。然而,还有一个小问题……当设置网格数据源时,之前actions/work我们通过使用网格的事件来管理每个Bank-Branch组合框单元格……当网格时没有发生DataSource
已设置。当每个 Customer 行都添加到网格中时,我们之前用于设置每个 Branch 组合框单元格的事件 DataSource
没有触发。
Branch 组合框将在组合框中显示正确的 selected Customer's Branch 值,但是,如果单击 Branch 组合框,您将看到自单个 Branch 组合框以来的所有分支单元格 DataSource
尚未设置。
所以,我们ned 另一种方法循环遍历网格的行集合,获取行 selected Bank,然后将 Branch 组合框单元格的 DataSource
设置为 selected Banks Branches 集合。我们只需要在设置网格数据源后执行一次。
private void SetAllBranchComboCellsDataSource() {
Customer curCust;
foreach (DataGridViewRow row in dataGridView1.Rows) {
if (!row.IsNewRow) {
curCust = (Customer)row.DataBoundItem;
BankCB bank = (BankCB)Banks.Where(x => x.BankID == curCust.BankID).FirstOrDefault();
// since we already checked for valid Bank values, we know the bank id is a valid bank id
DataGridViewComboBoxCell cbCell = (DataGridViewComboBoxCell)row.Cells["Branches"];
cbCell.DataSource = bank.Branches;
}
}
}
此更改后,网格最终更新 Load
方法可能如下所示。将网格 EditMode
设置为 EditOnEnter
将有助于单击组合框单元格一次以显示下拉列表。另外一个Button
被添加到表单到re-set用于测试的网格数据。
Random rand = new Random();
BindingList<BankCB> Banks;
BindingList<BranchCB> Branches;
BindingList<Customer> Customers;
ComboBox SelectedCombo;
private void Form3_Load(object sender, EventArgs e) {
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
dataGridView1.DefaultValuesNeeded += new DataGridViewRowEventHandler(dataGridView1_DefaultValuesNeeded);
SetNewData();
}
private void SetNewData() {
dataGridView1.Columns.Clear();
Setup_10_BanksWithRandomNumberOfBranches();
//Setup_10_BanksWith5BranchesNoDuplicates();
AddColumns();
Customers = GetCustomers();
CheckDataForBadComboBoxValues();
dataGridView1.DataSource = Customers;
SetAllBranchComboCellsDataSource();
}
private void btnNewData_Click(object sender, EventArgs e) {
SetNewData();
}
这应该完成了这个例子。然而,当分支数量与随机分支 selected 一起随机生成时,测试这方面的某些方面可能是一个挑战。换句话说,每次执行代码都会产生不同的数据。为了消除这种随机性,我创建了第二组数据,其中有 10 家银行和 50 家分行。每家银行恰好有五 (5) 家分行。此外,每个分行属于一家且仅一家银行。银行 1 有分行 1-5; Bank 2 有 Branch 6-10 等,所有 50 个 Branch 只使用一次。对于测试,使用此数据可能更容易。
调用此方法而不是 Setup_10_BanksWithRandomNumberOfBranches();
private void Setup_10_BanksWith5BranchesNoDuplicates() {
Branches = new BindingList<BranchCB>();
Branches.Add(BranchCB.BlankBranch);
for (int numOfBranches = 1; numOfBranches <= 50; numOfBranches++) {
Branches.Add(new BranchCB { BranchID = numOfBranches, BranchName = "Branch " + numOfBranches });
}
Banks = new BindingList<BankCB>();
BindingList<BranchCB> tempBranches;
BranchCB curBranch;
int branchIndex = 1;
for (int numOfBank = 1; numOfBank <= 10; numOfBank++) {
tempBranches = new BindingList<BranchCB>();
tempBranches.Add(BranchCB.BlankBranch);
for (int i = 0; i < 5; i++) {
if (branchIndex < Branches.Count) {
curBranch = Branches[branchIndex++];
tempBranches.Add(curBranch);
}
else {
break;
}
}
tempBranches = new BindingList<BranchCB>(tempBranches.OrderBy(x => x.BranchID).ToList());
Banks.Add(new BankCB { BankID = numOfBank, BankName = "Bank " + numOfBank, Branches = tempBranches });
}
}
很抱歉 post。 我希望这有意义并且有所帮助。