[英]Cannot set values from a DataGridViewComboBoxColumn to a bound DataGridView
I am having a DataGridView that initially loads data and shows it.我有一个 DataGridView,它最初加载数据并显示它。
When an user clicks an Edit Button, I am adding a DataGridViewComboBoxColumn by hiding one of the Columns.当用户单击编辑按钮时,我通过隐藏其中一个列来添加 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();
}
Here is the Button.Click
event where I am trying to add a ComboBox Column:这是我尝试添加 ComboBox 列的
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;
}
When I click on drop down, nothing happens:当我点击下拉菜单时,没有任何反应:
The DataTable used as the DataSource of your DataGridView has an auto-increment Column.用作 DataGridView 的数据源的 DataTable 有一个自动增量列。 You cannot use this Column as the
ProductId
, which can be changed by an User via the ComboBox selector.您不能将此 Column 用作
ProductId
,用户可以通过 ComboBox 选择器更改它。 It will make a mess (unless this was used just just to build a MCVE).它会弄得一团糟(除非它只是用来构建 MCVE)。
You can use this Column as the Primary Key - setting also Unique = true
.您可以将此列用作主键 - 也可以设置
Unique = true
。
Add instead a int
Column that represents the ProductId
Key, which links the ProductName
Column that is part of the DataTable set as the DataSource of the ComboBox Column.添加一个表示
ProductId
键的int
列,该列链接作为 DataTable 一部分的ProductName
列,该列设置为 ComboBox 列的数据源。
Since the ValueMember
property of the ComboBox is set to the ProductId
value and the ComboBox Column is bound to the ProductId
Column of the DataGridView DataTable, changing the SelectdItem of the ComboBox will change the value of the ProductId
Column of the DataTable used as data source of your DataGridView.由于 ComboBox 的
ValueMember
属性设置为ProductId
值,并且 ComboBox Column 绑定到 DataGridView DataTable 的ProductId
列,因此更改 ComboBox 的 SelectdItem 将更改用作数据源的 DataTable 的ProductId
列的值你的数据网格视图。
Added to the BindProductsGrid()
method:添加到
BindProductsGrid()
方法:
dtGrid.PrimaryKey = new[] { pkColumn };
dtGrid.AcceptChanges();
(mandatory) dgvProducts.Columns["ProductId"].ReadOnly = true;
anddgvProducts.AllowUserToAddRows = false;
(since this seems to be the intention: let the User specify the Product only using the ComboBox selector) The DataGridViewComboBoxColumn
is added after the DataSource of the DataGridView is set. DataGridViewComboBoxColumn
是在DataGridView 的DataSource 设置之后添加的。 This because this Column is bound to the ProductId
Column, as the corresponding Column of the DataGridView's DataTable.这是因为此 Column 绑定到
ProductId
列,作为 DataGridView 的 DataTable 的对应列。
It allows to add to the DataGridView two Columns bound to the same Column of the data source in code, without confusing the Control.它允许在代码中向 DataGridView 添加绑定到数据源的同一 Column 的两个 Column,而不会混淆Control。
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;
}
The DataTable used as data source of the ComboBox has the same two Columns, just AcceptChanges()
(optional) is added to the previous code:作为 ComboBox 数据源的 DataTable 具有相同的两列,只是在前面的代码中添加了
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;
}
Now, some adjustments to make the Product selection more responsive :现在,进行一些调整以使产品选择更具响应性:
(➨ note that the DataGridView is named dgvProducts
) (➨ 注意 DataGridView 被命名为
dgvProducts
)
The EditingControlShowing
handler subscribes to the ComboBox Cell SelectedIndexChanged
event EditingControlShowing
处理程序订阅 ComboBox Cell SelectedIndexChanged
事件
The ComboBox SelectedIndexChanged
handler invokes asynchronously Validate()
, to show the ComboBox selection immediately (no need to select another Cell to see it applied) ComboBox
SelectedIndexChanged
处理程序异步调用Validate()
,以立即显示 ComboBox 选择(无需选择另一个单元格来查看它的应用)
BeginInvoke(new Action(() => Validate()));
The DataGridView CellContentClick
handler changes the style of the ComboBox to DataGridViewComboBoxDisplayStyle.ComboBox
DataGridView
CellContentClick
处理程序将 ComboBox 的样式更改为DataGridViewComboBoxDisplayStyle.ComboBox
CellLeave
handler restores the ComboBox style to DataGridViewComboBoxDisplayStyle.Nothing
, so it looks like a TextBox. CellLeave
处理程序将 ComboBox 样式恢复为DataGridViewComboBoxDisplayStyle.Nothing
,因此它看起来像一个 TextBox。
▶ If you instead want to just hide/show the ProductName
column, you can do without the CellContentClick
and CellLeave
handlers and keep the initial ComboBox style. ▶ 如果您只想隐藏/显示
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;
}
}
}
This how it works using the code shown here:这是使用此处显示的代码的工作方式:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.