【问题标题】:Load different data into different rows of the same comboboxcolumn c#将不同的数据加载到同一个comboboxcolumn c#的不同行中
【发布时间】:2021-06-11 13:46:24
【问题描述】:

好的,所以我有一个带有两个组合框列的 dataGridView。

银行和银行分行,用户可以选择在组合框中添加几家银行,但分行列表取决于所选银行。

在第一行,这完美无缺。

在任何其他行上,选择存储体时,所有行上的所有分支列都将更新到该银行的分支列表。

我的问题是,我该如何做到这一点,以便在选择第二家或第三家银行时,仅更新该行的分行列表,而不更新所有其他行。

这就是我在玩的东西。

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);
                }

            }

【问题讨论】:

  • 发生这种情况的原因并不明显。至少显示有关如何将数据放入网格的代码,并显示当单元格的编辑器关闭时如何更新数据。
  • 技术上你不能。所有行必须具有相同的数据源,但您可以过滤数据视图,以便您只看到每行中您想看到的内容。执行此操作的代码会及时丢失,但希望提示可以帮助您入门。
  • 感谢@Crowcoder,我现在正在调查。
  • 您需要更新分行列表,在其中为该特定行分配您的银行。 foreach 行模型,添加分支列表并动态更新
  • @Ugur 请多放点光。我有点迷路了

标签: c# datagridview combobox windows-forms-designer


【解决方案1】:

一个常见的误解是DataGridViewComboBoxCell 的工作方式与普通的ComboBox 一样。在很多方面,它确实如此。然而;普通的ComboBoxDataGridViewComboBoxCell 宽容得多。例如,在代码中,如果您尝试将常规的 ComboBoxes 值设置为不在其项目列表中的内容……那么……什么也不会发生。不会引发错误/异常,并且根本没有设置值。 DataGridViewComboBoxCell 不是这种情况。将组合框单元格的值设置为不在其项目列表中的值将导致DataGridView 抛出其DataError 异常。

您可以通过简单地吞下/忽略网格DataError 来寻求最后的选择,但是,由于许多原因,这是一个糟糕的选择。特别是,在我们想要使用网格组合框的情况下,您可能无法忽略网格DataError,因为不断出现的错误最终可能会淹没 UI。

DataGridView中创建级联组合框的一种方法

正如其他人所评论的那样,组合框列的一种可能的解决方案是将每个组合框单元格包含不同的值......是将组合框“列”数据源设置为包含“所有”可能组合框的列表价值观。然后,将每个组合框“单元格”DataSource 单独设置为组合框列DataSource 的“过滤/子集”列表。这是下面完整示例中使用的方法。

我使用了您的银行分行类型的场景。具体来说,有 10 家不同的银行和 50 家不同的分行。每家银行可能有零个或多个分行。此外,一个分行可能属于多个银行,也就是说,许多银行可能有同一个分行。

为了测试,我们需要一个客户。客户将具有唯一的 ID、名称、BankID 和可能的 BranchID。这将用于在设置网格的DataSource 时测试组合框。但是,我们将把它分解为两个步骤。第一步是首先让两个组合框正常工作,不要设置网格数据源。第1步完成后,我们将进入第2步,处理设置网格数据源时的问题。

您可以创建一个新的 winforms 解决方案,将 DataGridViewButton 放到表单上,将 Button 命名为 btnNewData 并连接其 Click 事件。在如下所示的步骤中复制下面发布的代码,以完成如下所示的表单......

这会在加载数据时显示错误消息框。

显示过滤后的组合框。

第 1 步)设置组合框而不设置网格数据源。

对于此示例,将设置网格以保存客户数据,组合框列将包含该客户的银行和分行值。

首先,我们将创建一个名为 SelectedCombo 的全局“常规”ComboBox 变量。当用户点击银行组合框单元格时,网格EditingControlShowing 事件将触发,我们会将DataGridViewComboBoxCell 转换为“常规”ComboBox,即SelectedCombo。然后,订阅SelectedIndexChanged 事件。当SelectedIndexChanged 事件触发时,我们将设置随附的分支单元数据源。

我们将使用几个网格事件,下面是这段代码中使用的事件的简要说明。

EditingControlShowing 事件……

当用户单击网格单元格/组合框单元格并将该单元格置于“编辑”模式时触发。此事件在用户实际键入/更改单元格中的任何内容之前触发。如果编辑的单元格是 Banks 组合框单元格,则代码设置全局 SelectedCombo 变量并订阅其 SelectedIndexChanged 事件。

CellLeave 事件…

当用户试图“离开”一个单元格时触发。此事件仅用于“取消订阅”全局变量 SelectedCombo Combo Boxes SelectedIndexChanged 事件。否则,当用户单击分支组合框单元格之一时,该事件将不正确地触发。

DefaultValuesNeeded 事件…

当用户在网格的最后一个“新”行中键入内容或选择组合框值时触发。如果组合框的数据源没有“空/空”值,这可能会导致一些问题。因此,我们的想法是继续为组合框的“新”行提供一些默认值,即银行列表中的第一家银行和一个空的分行。

为了帮助我们,我们将创建三个简单的类。

BranchCB 类

代表一个分支并且有两个属性…一个intBranchIDstringBranchName。并重写ToString() 方法来调试输出。

此外,还有一个静态的BlankBranch 属性,它返回一个BranchCB 对象,其中BranchID 为0,一个空的string 作为BranchName。注意:使用DataGridViewComboBoxCell 的一个可能问题是单元格值变为空/空。网格可能会对此抱怨并抛出其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;
  }
}

BankCB 类

BankCB 类是直截了当的,一个 int BankID,一个 string BankName 和一个 BindingListBranchCB 对象。用于调试的重写 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();
  }
}

客户类

一个Customer 类具有一个int CustomerIDstring CustomerName 和两个用于BankIDBranchIDint 属性。该类用于测试组合框。

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 用于创建测试数据。

BanksBankCB 对象的列表,将用作Banks DataGridViewComboBoxColumnDataDource

Branches 是所有BranchCB 对象的列表,将用作Branches DataGridViewComboBoxColumnDataSource

CustomersCustomer 对象的列表,将用作DataGridViewDataSource

最后,SelectedCombo 是一个普通的ComboBox,如前所述使用。

创建一些测试随机银行和分行数据……

下面的代码创建并设置了全局变量BanksBranches 具有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);
  }
}

将列添加到网格中

设置网格银行组合框列应该相当简单,因为所有组合框都包含相同的数据。我们想将银行组合框列的ValueMember 属性设置为BankID,并将其DisplayMember 属性设置为BankName。对于 Branches 组合框列,ValueMember 将是 BranchIDDisplayMember 将是 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();
}

我们应该看到银行和分行组合框单元格,选择银行组合框应该显示 10 家银行,而分行组合框将显示所有 50 个分行以及“空白”分行。

过滤分支组合框单元格

要订阅的第一个网格事件是网格EditingControlShowing 事件。如果编辑的单元格“是”一个银行组合框单元格,那么我们希望将该银行组合框单元格转换为我们的全局SelectedComboComboBox 变量。然后让全局SelectedCombo 订阅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 选择已更改,并且我们想要设置随附的 Branch 单元格数据源。我们可以从全局SelectedCombo.SelectedItem 属性中获取选中的BankCB 对象。接下来,我们获取该行的 Branches 组合框单元格,并将 DataSource 设置为选定的 BankCBBranches 集合。最后,将 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;
  }
}

如果我们现在运行代码...您应该注意...在您单击银行组合框单元格之前...如果您单击分行组合框单元格,那么您将看到“所有”分行。但是,如果您选择/更改银行组合框的值,然后单击分行组合框单元格...您将在我们的ComboBox_SelectedIndexChanges 代码中收到转换错误。

问题是全局 ComboBoxes SelectedComboSelectedIndexChanged 事件仍然连接起来,并且会在选择分支组合框时触发……这是我们不想要的。当用户“离开”银行单元格时,我们需要从其SelectedIndexChanged 事件中取消订阅SelectedCombo。因此,连接网格 CellLeave 事件并取消订阅全局 SelectedCombo_SelectedIndexChanged 事件应该可以解决此问题。

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);
  }
}

如果我们现在运行代码……用户可以更改分支组合框而不会出错。但是,有一个可能的问题……“在”用户选择银行之前,用户可以单击“分行”组合框,由于尚未设置“银行”组合框,用户将看到“所有”分行并可以选择任何分支。这可能会使分行处于与选择的分行但未选择银行的不一致状态。我们不希望这种情况发生。

在此示例中,我们将连接网格DefaultValuesNeeded 事件,通过将 BankID 设置为银行列表中的第一个银行,将新行设置为“默认”状态。并将新行分支值设置为“空白”分支。这应该注意防止用户在没有“首先”选择银行的情况下选择分行。

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 个将具有有效的银行和分行值。 “客户 16”将有一个无效的银行编号,17 将有一个无效的分行,最后 18 将有一个有效的银行和一个有效的分行,但是,分行值将不在客户所选银行的分行集合中。

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),因此银行或分行组合框的值为空。如果您将光标滚动到这两个组合框上,您应该会看到数据错误不断触发,基本上我们需要解决这个问题。此外,如果您查看 bad Customer 18,您会注意到 Branch 组合框的值设置为不一致的状态……在我的特定测试中……Branch 1 不是 Bank 3 中的 Branch。

错误的原因很明显,但解决方案并不多。在这种情况下,我们有一个DataSource 到网格,其中组合框的数据不好,不幸的是,我们不能忽略这一点。我们必须做点什么。在这种情况下,没有“好的”选项,数据库中的数据显然已损坏,我们无法继续不做任何事情。所以...,您可以通过删除它来忽略该行...或者...您可以将错误值添加为新的组合框值...或者...您可以将错误值“更改”为默认/良好值。其他选项可能适用,但底线是……我们想继续,但我们必须首先对不良价值观做些什么。所以……选择你自己的毒药。

在此示例中,我将使用最后一个选项,并将错误值更改为默认/有效值,并向用户弹出一个消息框,让他们知道更改了什么,然后继续。

在设置网格的 DataSource 之前,我们必须检查客户数据中的组合框值。因此,创建了一个小方法,循环遍历Customers 列表并检查坏银行和坏分行值。如果发现错误的银行值,则银行值将设置为银行列表中的第一个银行。如果 Bank 值没问题,但 Branch 不好,那么我们将 Branch 设置为“空白”分支。

这看起来有很多代码;但是,大部分代码都在构建错误字符串。您可以简单地更改值并继续并且永远不要打扰用户。但是,至少,调试或日志语句将是用于调试目的的好主意。

首先检查BankID 是否有效,然后检查BranchID。如果其中一个是坏的,那么坏的值将被替换为默认/有效值。

请记住...由于每个 Branch 组合框单元格的项目列表基于选择的银行,因此,我们需要查看客户选择的银行的分行集合,并查看客户 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。但是,还有一个小问题……在设置网格数据源时,我们之前通过使用网格的事件来管理每个 Bank-Branch 组合框单元所做的操作/工作……在网格DataSource 被设置时没有发生放。将每个 Customer 行添加到网格时,我们之前用于设置每个 Branch 组合框单元格的 DataSource 的事件没有触发。

Branch 组合框将在组合框中显示正确选择的 Customer's Branch 值,但是,如果您单击 Branch 组合框,您将看到所有分支,因为单个 Branch 组合框单元格 DataSource 尚未还没定。

因此,我们需要另一种方法来循环遍历网格的行集合,获取选中的行 Bank,然后将 Branch 组合框单元格的 DataSource 设置为选中的 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 被添加到表单中以重新设置网格数据以进行测试。

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();
}

这应该完成示例。然而,当分支的数量与选择的随机分支一起随机生成时,测试这方面的某些方面可能是一个挑战。换句话说,每次执行代码时都会产生不同的数据。为了消除这种随机性,我创建了第二组数据,其中有 10 家银行和 50 家分行。每家银行正好有五 (5) 个分行。此外,每个分行都属于一家且仅一家银行。银行 1 有分行 1-5;银行 2 有分行 6-10 等,所有 50 个分行只使用一次。对于测试,使用这些数据可能更容易。

调用这个方法而不是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 });
  }
}

抱歉,帖子太长了。 我希望这是有道理的并有所帮助。

【讨论】:

  • 尊重答案!
猜你喜欢
  • 2015-01-27
  • 1970-01-01
  • 2014-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 1970-01-01
相关资源
最近更新 更多