用作 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;
}
}
}
这是使用此处显示的代码的工作原理: