我一直在寻找一种方法来执行以下操作,但无济于事。我想出了一个可行的解决方案,但我想知道是否有更好的方法来处理它。
问题:
我正在使用具有两个 DataGridViewComboBoxColumn、col1 和 col2 的 DataGridView。
col1 已将其 DataSource 设置为 DataTable。根据 col1 中给定单元格的选择,我希望同一行中的各个 col2 单元格将其 DataSource 设置为 col2 的 DataSource 的过滤 DataView。
我当前实现的缩写代码可能更好地帮助描述我正在尝试做的事情:
DataGridView dg = new DataGridView();
dg.DataSource = new BindingSource() { DataSource = _myDataTable }; //DataTable with Foreign Keys for ID1 and ID2
dg.CellValueChanged += new DataGridViewCellEventHandler(dg_CellValueChanged);
DataGridViewComboBoxColumn col1 = new DataGridViewComboBoxColumn();
col1.DataPropertyName = "ID1";
col1.DisplayMember = "Display1";
col1.ValueMember = "ID1";
col1.DataSource = dataTable1;
col1.ValueType = typeof(Int32);
DataGridViewComboBoxColumn col2 = new DataGridViewComboBoxColumn();
col2.DataPropertyName = "ID2";
col2.DisplayMember = "Display2";
col2.ValueMember = "ID2";
col2.DataSource = dataTable2;
col2.ValueType = typeof(Int32);
dg.Columns.Add(col1);
dg.Columns.Add(col2);
然后我将事件处理程序定义为:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
if (dgv == null)
return;
int selectedID;
if (!int.TryParse(dgv[e.ColumnIndex, e.RowIndex].Value.ToString(), out selectedID))
return;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
var col = dgv.Columns[e.ColumnIndex + 1] as DataGridViewComboBoxColumn;
if(col == null)
return;
var dt = col.DataSource as DataTable; // The macro-DataTable containing all possible values
if(dt == null)
return;
DataView dv = new DataView(dt, "ID1 = " + selectedID, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
//This is the part that I am wondering if there is a better way of handling
cell.DataSource = dt; //Set the data source to the macro-DataTable so that when we set the Value to something in the new DataView it will not throw an exception
cell.DisplayMember = "Display2"; // Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.Value = dv[0]["ID2"]; // Set the value to the first option in the new DataView to avoid an exception being thrown when setting the dv as the DataSource
cell.DataSource = dv;
}
}
最后一部分是我关心的。动态切换cell2的DataSource时,如果cell2的当前选择没有出现在新的DataView中,那么会抛出异常:
System.ArgumentException:DataGridViewComboBoxCell 值无效。
为了避免这种情况,我将数据源设置为宏数据表(包含所有可以根据单元格1的选择显示的值),将单元格2的选定值更改为数据视图中的第一个结果,然后设置单元格2的数据源要说DataView。这一切都确保了单元格永远不会有无效的选择,并且可以按预期工作。
我的问题是,有没有更好/更简单的方法来做到这一点?就我的使用而言,此代码仅在创建新行时被激活,因此在给定表单中它不会被执行多次。但是,如果有更好的方法或改进它的一些建议,我将不胜感激!
(这里的第一个问题,所以我也对发布的建议持开放态度......对任何失误表示歉意)
编辑(提供表结构 - 还将“BoundID”更改为“ID1”以避免混淆):
DataGrid 的表结构是:
MainTableID 整数
ID1 int -- 这是 col1 的外键
ID2 int -- 这是 col2 的外键
第1列的表结构:
ID1 整数
显示 1 varchar(50)
第2列的表结构:
ID2 整数
显示2 varchar(50)
ID1 int -- 这是 col1 的外键
更新: 我根据 Mohsen 的建议重新定义了 CellValueChanged 事件处理程序:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
DataView dv = new DataView( ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource), "ID1 = " + dgv.CurrentCell.Value, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
cell.DisplayMember = "Display2"; // Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.DataSource = dv;
}
}
我已经为 DataError 添加了一个事件处理程序,就像他建议的那样:
void dg_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
if(e.ColumnIndex != 1)
{
//Alert the user for any other DataError's outside of the column I care about
MessageBox.Show("The following exception was encountered: " + e.Exception);
}
}
这似乎完美无缺。