【问题标题】:c# Populate datagridview based on ComboBox Item Selectedc#根据ComboBox Item Selected填充datagridview
【发布时间】:2021-09-13 08:18:33
【问题描述】:

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

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

所以如果我选择“回扣”,我需要在文本框单元格中看到来自 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 个新问题。

我正在尝试从 db 加载已保存的锻炼,但是当我这样做时,没有视频链接填充 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 次,则选择了一个随机项目,但另一列中没有出现视频链接):

error

【问题讨论】:

  • 也许用两列数据填充组合框。 ComboBox 具有属性 DisplayMember 和 ValueMember。然后您可以将 selectedValue 复制到另一列。
  • @benuto 所以,你建议在显示成员中用“nome”填充组合框,在值成员中用“link_video”填充,然后我可以用值成员的链接视频填充文本框吗?我是 C# 的新手。你能告诉我怎么做吗?
  • 查找有关ComboBox.DataSource和onChange事件的信息,这是一个相当简单的话题
  • @benuto 我正在搜索大约 3 天,但我无法解决它
  • 1.在数据库中查询名称和视频链接,将它们存储在列表中(例如) 2. 将列表分配给 combo.datasource 3 设置组合成员 4. 将方法添加到 onSelectedValueChanged 5. 瞧!

标签: c# datagridview datagridviewcomboboxcell


【解决方案1】:

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

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

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

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

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 事件触发,然后使用适当的视频链接更新视频单元格数据。

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

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

因此,鉴于此,grids 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 更改了,我们知道我们需要更改“视频”单元格。

同样有很多方法可以做到这一点,但是,如果我们将网格的“Exercise”组合框列的DataSource 设置为ExerciseAndVideo 对象的列表,那么我们应该能够得到那个特定的ExerciseAndVideo直接来自全局 ComboBox curCombo 的对象。这将告诉我们将“哪个”视频放置在“视频”单元格中。这可能看起来像……

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

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

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

因此,在这种情况下,您需要一种方法来遍历网格中的所有行,检查该行的练习值,然后将视频单元格值设置为正确的视频链接。由于您没有说网格是否有数据源,我假设这就是您所需要的。如果有数据源,我建议您检查每个“练习”以确保数据中的“练习”在项目的组合框列表中,如果一个或多个“练习”在不在数据中的数据中组合框列项目列表,那么当您尝试设置网格数据源时,您将获得网格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;
        }
      }
    }
  }
}

【讨论】:

  • 非常感谢!非常好的和完整的解释。不幸的是,我使用之前的答案完成了它,现在我正在尝试使用我描述的 iTextsharp 解决问题。
  • 现在我也在测试你的代码。我确实在新表单中复制粘贴,但是当我选择练习名称时,我在链接栏中看不到任何链接。我在调试中没有错误。我错过了什么?
  • 我只能假设您没有正确订阅网格两个事件。我在表单加载事件中添加了代码。
  • 是的,这是真的。我通过设置不使用表单加载代码的表单加载属性解决了,非常感谢!您的解决方案还解决了 iTextsharp 的问题。
  • 我正在使用一个按钮通过 itextsharp 将数据网格保存在 pdftable 中。如果我想识别像“if (dataGridView1.Columns[e.ColumnIndex].Name == "Video")”这样的链接列,我该如何在按钮点击功能中做到这一点?
【解决方案2】:

我将 ComboBox 与 DatagridViewComboboxColumn 混合在一起。 部分原因是你的错:)。

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

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

【讨论】:

  • 是的,是的!通过一些更改,它可以正常工作。现在我有另一个问题.. LOL :) 填充 datagridview 后,我用 iTextsharp pdf 创建了一个 pdf。在组合中使用 display 和 value 成员之前,它是在 pdf 表中保存练习的名称。现在改为保存组合的值成员。填充 pdf 表的代码是这样的: foreach (DataGridViewRow row in dataGridView1.Rows) {foreach (DataGridViewCell cell in row.Cells) {pdfTable.AddCell(Convert.ToString(cell.Value));}} 你有吗想法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-27
  • 2013-05-11
  • 2017-09-13
  • 1970-01-01
相关资源
最近更新 更多