无法将 DataGridViewComboBoxColumn 中的值设置为绑定的 DataGridView

Posted

技术标签:

【中文标题】无法将 DataGridViewComboBoxColumn 中的值设置为绑定的 DataGridView【英文标题】:Cannot set values from a DataGridViewComboBoxColumn to a bound DataGridView 【发布时间】:2021-09-22 17:41:57 【问题描述】:

我有一个 DataGridView,它最初会加载数据并显示它。 当用户单击编辑按钮时,我通过隐藏其中一个列来添加 DataGridViewComboBoxColumn。

private DataTable BindCombo()

    DataTable dt = new DataTable();
    dt.Columns.Add("ProductId", typeof(int));
    dt.Columns.Add("ProductName", typeof(string));
    dt.Rows.Add(1, "Product1");
    dt.Rows.Add(2, "Product2");
    return dt;


private void BindGrid()

   DataTable dtGrid = new DataTable();
   DataColumn column = new DataColumn("ProductId")
   
      DataType = System.Type.GetType("System.Int32"),
      AutoIncrement = true,
      AutoIncrementSeed = 1,
      AutoIncrementStep = 1
   ;
   dtGrid.Columns.Add(column);
   dtGrid.Columns.Add("ProductName", typeof(string));

   dtGrid.Rows.Add(null, "Product1");
   dtGrid.Rows.Add(null, "Product2");

   dataGridView1.DataSource = dtGrid;
 

 private void Form1_Load(object sender, EventArgs e)
 
    BindGrid();
 

这是我尝试添加组合框列的Button.Click 事件:

 private void btnEdit_Click(object sender, EventArgs e)
 
     dataGridView1.AllowUserToAddRows = true;
     dataGridView1.ReadOnly = false;
     dataGridView1.Columns[1].Visible = false;
     DataGridViewComboBoxColumn col1 = new DataGridViewComboBoxColumn
     
         DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton,
         HeaderText = "Product Name",
         DataSource = BindCombo(),
         ValueMember = "ProductId",
         DisplayMember = "ProductName",
         DataPropertyName = "ProductId"
     ;
     dataGridView1.Columns.Add(col1);
     dataGridView1.AllowUserToAddRows = false;
    

当我点击下拉菜单时,什么也没有发生:

【问题讨论】:

【参考方案1】:

用作 DataGridView 的 DataSource 的 DataTable 有一个自增列。您不能将此列用作ProductId,用户可以通过组合框选择器更改它。它会弄得一团糟(除非它只是用于构建 MCVE)。 您可以将此列用作主键 - 也可以设置 Unique = true

改为添加一个代表ProductId 键的int 列,它链接作为组合框列的数据源的DataTable 的一部分的ProductName 列。 由于 ComboBox 的 ValueMember 属性设置为 ProductId 值,并且 ComboBox Column 绑定到 DataGridView 数据表的 ProductId Column,因此更改 ComboBox 的 SelectdItem 将更改 ProductId Column 的值用作 DataGridView 的数据源的 DataTable。

添加到 BindProductsGrid() 方法中:

dtGrid.PrimaryKey = new[] pkColumn ; dtGrid.AcceptChanges();(必填) dgvProducts.Columns["ProductId"].ReadOnly = true;dgvProducts.AllowUserToAddRows = false;(因为这似乎是意图:让用户仅使用 ComboBox 选择器指定产品)

DataGridViewComboBoxColumn是在DataGridView的DataSource设置后添加的。这是因为这个Column绑定到ProductIdColumn,作为DataGridView的DataTable的对应Column。 它允许在代码中向 DataGridView 添加绑定到数据源同一列的两个列,而不会混淆控件。

private void BindProductsGrid()

    var dtGrid = new DataTable();
    var pkColumn = new DataColumn("ID") 
        DataType = typeof(int),
        AutoIncrement = true,
        AutoIncrementSeed = 1,
        AutoIncrementStep = 1,
        Unique = true
    ;

    var productColumn = new DataColumn("ProductId") 
        DataType = typeof(int),
        Caption = "Product Id"
    ;

    dtGrid.Columns.Add(pkColumn);
    dtGrid.Columns.Add(productColumn);

    dtGrid.Rows.Add(null, 1);
    dtGrid.Rows.Add(null, 2);
    dtGrid.Rows.Add(null, 3);
    dtGrid.Rows.Add(null, 4);

    dtGrid.PrimaryKey = new[]  pkColumn ;
    dtGrid.AcceptChanges();

    dgvProducts.DataSource = dtGrid;

    dgvProducts.Columns["ID"].Visible = false;
    dgvProducts.Columns["ProductId"].ReadOnly = true;

    var productName = new DataGridViewComboBoxColumn 
        DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing,
        Name = "ProductName",
        HeaderText = "Product Name",
        ValueMember = "ProductId",
        DisplayMember = "ProductName",
        DataSource = BindCombo(),
        DataPropertyName = "ProductId",
        DisplayIndex = 2
    ;
    dgvProducts.Columns.Add(productName);
    dgvProducts.AllowUserToAddRows = false;

作为ComboBox的数据源的DataTable有两个相同的Column,只是在前面的代码中增加了AcceptChanges()(可选):

private DataTable BindCombo()

    var dt = new DataTable();
    dt.Columns.Add("ProductId", typeof(int));
    dt.Columns.Add("ProductName", typeof(string));

    dt.Rows.Add(1, "Product1");
    dt.Rows.Add(2, "Product2");
    dt.Rows.Add(3, "Product3");
    dt.Rows.Add(4, "Product4");
    dt.AcceptChanges();
    return dt;

现在,进行一些调整以使产品选择更具响应性: (➨ 请注意,DataGridView 被命名为 dgvProducts

EditingControlShowing 处理程序订阅 ComboBox Cell SelectedIndexChanged 事件

ComboBox SelectedIndexChanged 处理程序异步调用 Validate(),以立即显示 ComboBox 选择(无需选择另一个 Cell 即可看到它已应用)

BeginInvoke(new Action(() => Validate()));

DataGridView CellContentClick 处理程序将 ComboBox 的样式更改为 DataGridViewComboBoxDisplayStyle.ComboBox

CellLeave 处理程序将 ComboBox 样式恢复为 DataGridViewComboBoxDisplayStyle.Nothing,因此它看起来像一个 TextBox。

▶ 如果您只想隐藏/显示 ProductName 列,则可以不使用 CellContentClickCellLeave 处理程序并保留初始 ComboBox 样式。

private void dgvComboBox_SelectedIndexChanged(object sender, EventArgs e)

    (sender as ComboBox).SelectedIndexChanged -= dgvComboBox_SelectedIndexChanged;
    BeginInvoke(new Action(() => Validate()));


private void dgvProducts_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)

    if (e.Control is ComboBox cbo) 
        cbo.SelectedIndexChanged += dgvComboBox_SelectedIndexChanged;
    


private void dgvProducts_CellContentClick(object sender, DataGridViewCellEventArgs e)

    if (e.RowIndex > 0 && e.ColumnIndex == 2) 
        if (dgvProducts[2, e.RowIndex] is DataGridViewComboBoxCell cbox) 
            cbox.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox;
        
    


private void dgvProducts_CellLeave(object sender, DataGridViewCellEventArgs e)

    if (e.RowIndex < 0) return;
    if (dgvProducts.Columns[e.ColumnIndex].Name == "ProductName") 
        if (dgvProducts["ProductName", e.RowIndex] is DataGridViewComboBoxCell cbox) 
            cbox.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
        
    

这是使用此处显示的代码的工作原理:

【讨论】:

以上是关于无法将 DataGridViewComboBoxColumn 中的值设置为绑定的 DataGridView的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法正确获取图像数据或无法将数据发送到服务器?

无法将 createdAt 和 updatedAt 保存为日期时间值,也无法将后端保存为前端

C# 无法将类型为“System.Byte[]”的对象强制转换为类型“System.Data.DataTable

无法将类型为“System.Collections.Generic.List`1[EPMS.Domain.SingleItem]”的对象强制转换为类型“EPMS

无法将 .json 文件从 CSV 下载到 JSON 转换并且无法将 JSON 转换为 CSV

无法将 ReactiveUI 添加到 NUnit 测试项目