【问题标题】:Cannot set values from a DataGridViewComboBoxColumn to a bound DataGridView无法将 DataGridViewComboBoxColumn 中的值设置为绑定的 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;
    }

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

【问题讨论】:

    标签: c# .net winforms data-binding datagridview


    【解决方案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 列,则可以不使用 CellContentClick 和 CellLeave 处理程序并保留初始 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;
            }
        }
    }
    

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

    【讨论】:

      猜你喜欢
      • 2010-09-25
      • 2014-04-06
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 2015-09-05
      • 2017-03-30
      • 1970-01-01
      相关资源
      最近更新 更多