我一直在寻找一种方法来执行以下操作,但无济于事。我提出了一个有效的解决方案,但我想知道是否有更好的方法来处理它。
问题:
我使用的DataGridView有两个DataGridViewComboBoxColumn,col1和col2。
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值无效。
为了避免这种情况,我将数据源设置为宏-DataTable(包含可以基于cell1的选择显示的所有值),将选定的cell2值更改为DataView中的第一个结果,然后将cell2的DataSource设置为DataView。这一切都确保了单元格永远不会有无效选择,并且它按预期工作。
我的问题是,有更好/更简单的方法吗?对于我的使用,此代码仅在创建新行时激活,因此在给定的表单中不会执行多次。但是,如果有更好的方法或更好的方法,我会很感激!
(这里的第一个问题,所以我也愿意接受发布......为任何失误道歉的建议)
编辑(提供表格结构 - 也将“BoundID”更改为“ID1”以避免混淆):
DataGrid的表结构将是:
MainTableID int
ID1 int - 这是col1的外键
ID2 int - 这是col2的外键
第1列的表结构:
ID1 int
Display1 varchar(50)
第2列的表结构:
ID2 int
Display2 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);
}
}
这似乎完美无缺。
答案 0 :(得分:4)
您的代码可以简化为此,无异常抛出:
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;
cell.DataSource = ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource).Select("BoundID = " + dgv.CurrentCell.Value);
}
<强>更新强>
当您更改第一个组合框中已设置的项目,因为第二个组合框中的过滤后的数据视图具有不同的ID1
时,会引发异常,并且“ DataGridViewComboBoxCell值无效。” 。为了捕获此异常寄存器datagridview DataError
事件,注册方法中没有代码。然后,当您更改已设置的梳理器时,相应的组合框将填充正确的项目。
答案 1 :(得分:0)
这是适用于我的解决方案的快速停止商店,但我仍然希望给予Mohsen信誉。在原始问题中重复了这一点。
我根据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);
}
}
这似乎完美无缺。