c# 根据选择的 ComboBox 项填充 datagridview

c# Populate datagridview based on ComboBox Item Selected

我有这个数据网格,其中组合框是从 Db 填充的。

我想要实现的是,当我 select 列“Esercizio”中的某些内容时,“视频”列的单元格会自动填充“link_video”中的相应值Db 的列。

因此,如果我 select“回扣”,我需要在文本框单元格中查看 link 来自 db 的回扣视频。

这是我用来在加载表单时填充组合框的代码:

private void Myform_Load(object sender, EventArgs e) {

        con = new SqlConnection("Data Source=(LocalDB)\etc");
        cmd = new SqlCommand();
        con.Open();
        cmd.Connection = con;
        cmd.CommandText = "SELECT * FROM Esercizi";
        dr = cmd.ExecuteReader();

        while (dr.Read())
        {
          //populate Column1 combobox with "nome" column from Esercizi db table
            Column1.Items.Add(dr["nome"]);
            
        }

        con.Close();
    }

datagridview

编辑 我已经解决了 2 个新问题。

我正在尝试从数据库加载保存的锻炼,但是当我这样做时,没有视频 link 填充 dgv,因为网格事件没有触发。

我尝试的是向新的 selectionindexchanged 函数添加一个 foreach 循环,并在加载按钮代码的末尾触发它,如下所示:

private void curCombo_LoadedValues(object sender, EventArgs e)
        {

            foreach (DataGridViewRow row in dataGridView1.Rows)
            {
                foreach (DataGridViewCell cell in row.Cells)
                {
                    if (curCombo != null && curCombo.SelectedValue != null)
                    {
                        ExerciseAndVideo selectedExercise = (ExerciseAndVideo)curCombo.SelectedItem;
                        dataGridView1.CurrentRow.Cells["Video"].Value = selectedExercise.Video;

                    }
                }
            }
        }

private void button9_Click(object sender, EventArgs e){

    string connectionString = "Data Source=(LocalDB)\etc";
    string sql = "SELECT * FROM Schede WHERE Id = 6 AND dgv = 'dataGridView1'";
    SqlConnection connection = new SqlConnection(connectionString);
    SqlDataAdapter dataadapter = new SqlDataAdapter(sql, connection);using (DataTable dt = new DataTable())
    {
        dataadapter.Fill(dt);

        //Set AutoGenerateColumns False
        dataGridView1.AutoGenerateColumns = false;

        //Set Columns Count
        dataGridView1.ColumnCount = 6;

        //Add Columns

        dataGridView1.Columns[0].Name = "Esercizio";
        dataGridView1.Columns[0].HeaderText = "Esercizio";
        dataGridView1.Columns[0].DataPropertyName = "Esercizio";

        dataGridView1.Columns[1].Name = "Serie";
        dataGridView1.Columns[1].HeaderText = "Serie";
        dataGridView1.Columns[1].DataPropertyName = "Serie";

        dataGridView1.Columns[2].HeaderText = "Ripetizioni";
        dataGridView1.Columns[2].Name = "Ripetizioni";
        dataGridView1.Columns[2].DataPropertyName = "Ripetizioni";

        dataGridView1.Columns[3].Name = "Recupero";
        dataGridView1.Columns[3].HeaderText = "Recupero";
        dataGridView1.Columns[3].DataPropertyName = "Recupero";

        dataGridView1.Columns[4].Name = "Time Under Tension";
        dataGridView1.Columns[4].HeaderText = "Time Under Tension";
        dataGridView1.Columns[4].DataPropertyName = "Time_Under_Tension";

        dataGridView1.DataSource = dt;

        connection.Close();

        
    }

    curCombo_LoadedValues();
}

但是我得到这个错误“没有强制参数发送者的参数...

如何正确调用?

第二个问题是,当我像这样填充一些 dgv 列时,组合停止正常工作并且我在组合框上收到错误异常:

dataGridView1.Rows.Add(7);
            Random rnd = new Random();
            
            dataGridView1.Rows[0].Cells[1].Value = 3;
            dataGridView1.Rows[0].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[0].Cells[3].Value = 1;
            dataGridView1.Rows[0].Cells[4].Value = 201;
            
            dataGridView1.Rows[1].Cells[1].Value = 2;
            dataGridView1.Rows[1].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[1].Cells[3].Value = 1;
            dataGridView1.Rows[1].Cells[4].Value = 201;
            
            dataGridView1.Rows[2].Cells[1].Value = 3;
            dataGridView1.Rows[2].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[2].Cells[3].Value = 1;
            dataGridView1.Rows[2].Cells[4].Value = 201;
            
            dataGridView1.Rows[3].Cells[1].Value = 4;
            dataGridView1.Rows[3].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[3].Cells[3].Value = 1;
            dataGridView1.Rows[3].Cells[4].Value = 201;
            
            dataGridView1.Rows[4].Cells[1].Value = 5;
            dataGridView1.Rows[4].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[4].Cells[3].Value = 1;
            dataGridView1.Rows[4].Cells[4].Value = 201;
            
            dataGridView1.Rows[5].Cells[1].Value = 6;
            dataGridView1.Rows[5].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[5].Cells[3].Value = 1;
            dataGridView1.Rows[5].Cells[4].Value = 201;
            
            dataGridView1.Rows[6].Cells[1].Value = 7;
            dataGridView1.Rows[6].Cells[2].Value = rnd.Next(1, 13);
            dataGridView1.Rows[6].Cells[3].Value = 1;
            dataGridView1.Rows[6].Cells[4].Value = 201;

这是 dgv 现在的样子: dgv

这是组合停止正常工作后我得到的错误(我点击但没有出现下拉菜单,或者如果我点击 2-3 次,随机项目得到 selected 但没有视频 link出现在另一列):

error

我将 ComboBox 与 DatagridViewComboboxColumn 混合使用。 这部分是你的错 :).

这里有一个包含事件的表单。由于 CellValueChanged 在单元格退出时触发,我添加了一个 Dirty StateEvent 来更新视频列。 从设计师那里,只需将数据网格放在表单中并确保名称相同。 恕我直言,这 3 件事很关键

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            Init();
        }

        private void Init()
        {
            var list = new List<Exercise>() {
            new Exercise (){Name="Name1", Link= "Link1" },
            new Exercise (){Name="Name3", Link= "Link3" },
            new Exercise (){Name="Name4", Link= "Link4" },

            };

            var comboColumn = new DataGridViewComboBoxColumn() { Name = "ExerciseName", CellTemplate = new DataGridViewComboBoxCell() };
            comboColumn.DisplayMember = nameof(Exercise.Name);
            comboColumn.ValueMember = nameof(Exercise.Link);
            comboColumn.DataSource = list;
            
            dataGridView1.Columns.Add(comboColumn);
            dataGridView1.Columns.Add(new DataGridViewTextBoxColumn() { Name = "Video" });
            dataGridView1.CellContentClick += DataGridView1_CellContentClick;
            dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
            dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
        }

        private void DataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            var currentCell = (sender as DataGridView).CurrentCell;
            if(currentCell.ColumnIndex == 0)
                dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
            
        }

        private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex != 0)
                return;
            var comboCell = (dataGridView1.Rows[e.RowIndex].Cells[0] as DataGridViewComboBoxCell);
            var value = comboCell.Value;
            dataGridView1.Rows[e.RowIndex].Cells["Video"].Value = value;

        }

        private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            
        }
    }

    public class Exercise
    {
        public string Name { get; set; }
        public string Link { get; set; }
    }

在发布的代码中,它显示了对数据库的查询以获取练习名称并将这些名称添加为项目以显示在该列的“每个”组合框单元格中。这很好,但是,关于“哪个视频”属于组合框项目列表中的每个项目的信息为零 (0)。我将假设视频与哪个练习相关的“关系”将涉及对数据库的另一个查询。

如果是这种情况,那么很容易看出,当网格中的组合框发生变化时,您可以简单地查询数据库以查找用于所选练习的视频。这肯定行得通;但是,它会产生冗余问题。例如,假设用户在表格第 1 行的组合框中选择了“kicks”。该代码查询数据库并获取“踢球”视频的位置,并将“视频”单元格设置为此视频路径。然后,用户再次为其他行选择“踢球”。这将重新查询数据库以获取我们之前获得的相同数据。您希望避免不必要地查询数据库。

鉴于此,避免不必要地重新查询数据库似乎是一种更好的方法,即我们以某种方式将练习与练习使用的特定视频“结合”。您可以在加载表单时执行此操作一次。完成练习后,循环遍历每个练习并在数据库中查询该练习视频,并将其与练习结合起来。获得视频 link 后,我们“保存”此信息。使用这种方法,您不必为任何给定的练习重新查询数据库,因为我们已经为所有练习保存了该信息。

有无数种方法可以将练习与视频“结合”起来。一种可能的解决方案是创建一个具有这两个属性的 Class。我们称它为 Class ExerciseAndVideo... 它有两个属性,一个 string Exercise 是练习文本,一个 string Video 定义了Exercise 的视频路径。这个简单的 class 可能看起来像……

public class ExerciseAndVideo {
  public string Exercise { get; set; }
  public string Video { get; set; }
}

这是一种将 Exercise 与特定 Video“组合”的方法。然后我们可以列出 ExerciseAndVideo 个对象,例如...

List<ExerciseAndVideo> ExerciseList;

然后我们不仅可以将此列表用作组合框列的 DataSource,还可以将此列表用作轻松分辨“哪个”视频属于“哪个”练习的方式。下面的示例使用此策略。

如何在 DataGridView

中实现

要记住的一件事是 DataGridViewComboBoxCell 与“普通”ComboBox 是“不同的怪物”。例子;对于常规组合框,有一个事件称为... SelectedIndexChanged 事件。当用户“更改”组合框当前选择的索引时会触发此事件。另一方面,DataGridViewComboBoxCell 没有 SelectedIndexChanged 事件,我想您可能知道这一点。网格没有这个事件是有道理的,因为网格的组合框“列”可能有很多组合框。

幸运的是,即使 DataGridViewComboBoxCell 没有 SelectedIndexChanged 事件,我们也可以将“单个”组合框单元格转换为 ComboBox(在本例中为组合框正在编辑的单元格),那么我们可以订阅那个 ComboBoxes SelectedIndexChanged 事件。然后我们可以简单地等到 SelectedIndexChanged 事件触发,然后用适当的视频更新视频单元格数据 link.

DataGridView为此提供了一个特殊事件,称为...EditingControlShowing。当用户开始“编辑”网格中的单元格时,将触发此事件。在这种特殊情况下,当用户单击组合框单元格并“开始”更改单元格值时,将触发此事件。我们想在这个事件中做的是简单地将组合框单元格转换为常规 ComboBox...,然后订阅我们将在下面实现的 ComboBoxes SelectedIndexChanged 事件。

策略是这样的……我们将创建一个“全局”ComboBox 变量,我们将调用 curCombo。当网格 EditingControlShowing 事件触发时,我们看到编辑的单元格是一个“练习”单元格……然后我们将把网格中的那个组合框单元格转换为全局 curCombo 变量。然后我们将订阅那个组合框 SelectedIndexChanged 事件。当用户“离开”单元格时,我们将取消订阅全局变量 curCombo SelectedIndexChanged 事件以保持干净。

因此,鉴于此,网格 EditingControlShowing 事件可能如下所示...

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
  if (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name == "Exercise") {
    curCombo = e.Control as ComboBox;
    if (curCombo != null) {
      curCombo.SelectedIndexChanged -= new EventHandler(curCombo_SelectedIndexChanged);
      curCombo.SelectedIndexChanged += new EventHandler(curCombo_SelectedIndexChanged);
    }
  }
}

显然我们需要实现事件处理程序curCombo_SelectedIndexChanged。在这种情况下,我们会知道之前用户选择了一个组合框单元格并将该单元格中的值更改为其他值。由于 Exercise 已更改,我们知道我们需要更改“视频”单元格。

同样,您可以通过多种方式执行此操作,但是,如果我们将网格的“练习”组合框列的 DataSource 设置为 ExerciseAndVideo 对象的列表,那么我们应该能够直接从全局 ComboBox curCombo 获取那个特定的 ExerciseAndVideo 对象。这将告诉我们将“哪个”视频放置在“视频”单元格中。这可能看起来像……

private void curCombo_SelectedIndexChanged(object sender, EventArgs e) {
  if (curCombo != null && curCombo.SelectedValue != null) {
    ExerciseAndVideo selectedExercise = (ExerciseAndVideo)curCombo.SelectedItem;
    dataGridView1.CurrentRow.Cells["Video"].Value = selectedExercise.Video;
  }
}

private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e) {
  if (dataGridView1.Columns[e.ColumnIndex].Name == "Exercise") {
    if (curCombo != null) {
      curCombo.SelectedIndexChanged -= new EventHandler(curCombo_SelectedIndexChanged);
    }
  }
}

要完成示例并将所有这些放在一起可能会产生如下所示的结果……

List<ExerciseAndVideo> ExerciseList;
ComboBox curCombo;

public Form2() {
  InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e) {
  ExerciseList = GetExerciseVideoComboBoxListFromDB();
  dataGridView1.Columns.Add(GetExcerciseComboBoxColumn(ExerciseList));
  dataGridView1.Columns.Add(GetLinkColumn());
  dataGridView1.CellLeave += new DataGridViewCellEventHandler(dataGridView1_CellLeave);
  dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
}


private DataGridViewComboBoxColumn GetExcerciseComboBoxColumn(List<ExerciseAndVideo> exerciseData) {
  DataGridViewComboBoxColumn cbCol = new DataGridViewComboBoxColumn();
  cbCol.HeaderText = "Exercise";
  cbCol.Name = "Exercise";
  cbCol.DisplayMember = "Exercise";
  cbCol.DataSource = exerciseData;
  return cbCol;
}

private DataGridViewLinkColumn GetLinkColumn() {
  DataGridViewLinkColumn col = new DataGridViewLinkColumn();
  col.HeaderText = "Video";
  col.Name = "Video";
  col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
  return col;
}

private List<ExerciseAndVideo> GetExerciseVideoComboBoxListFromDB() {
  List<ExerciseAndVideo> exerciseList = new List<ExerciseAndVideo>();
  exerciseList.Add(new ExerciseAndVideo { Exercise = "Crosses", Video = @"C:/somepath/Crosses.htm/" });
  exerciseList.Add(new ExerciseAndVideo { Exercise = "Kickback", Video = @"C:/somepath/Kickback.htm" });
  exerciseList.Add(new ExerciseAndVideo { Exercise = "Leg Extensions", Video = @"C:/somepath/LegExtensions.htm" });
  exerciseList.Add(new ExerciseAndVideo { Exercise = "Crunches", Video = @"C:/somepath/Crunches.htm" });
  exerciseList.Add(new ExerciseAndVideo { Exercise = "Pushups", Video = @"C:/somepath/Pushups.htm" });
  return exerciseList;
}

如果网格有数据源怎么办?

当网格没有数据源时,这按预期工作。但是,如果网格有一个数据源,并且数据源中的 columns/properties 之一绑定到我们的“Exercise”组合框列,那么,会发生什么……是在加载数据后,组合框框应显示正确的练习,但是,所有视频单元格将保持为空。这显然是因为加载数据时未触发网格事件。

因此,在这种情况下,您需要一种循环遍历网格中所有行的方法,检查该行的练习值,然后将视频单元格值设置为正确的视频 link.由于您没有说明网格是否有数据源,因此我假设这就是您所需要的。如果有数据源,我建议你检查每个“Exercise”以确保数据中的“Exercises”在组合框项目列表中,如果一个或多个“Exercises”在数据中不在组合框列项目列表,那么当您尝试设置网格数据源时,您将获得网格 DataError

我希望这是有道理的。

编辑...数据加载到网格后设置视频单元格的示例。

private void SetVideoCellsAfterDataLoad() {
  foreach (DataGridViewRow row in dataGridView1.Rows) {
    if (!row.IsNewRow && row.Cells["Exercise"].Value != null) {
      foreach (ExerciseAndVideo eav in ExerciseList) {
        if (row.Cells["Exercise"].Value.ToString() == eav.Exercise) {
          row.Cells["Video"].Value = eav.Video;
          break;
        }
      }
    }
  }
}