我一直在寻找执行以下操作的方法,但无济于事。我想出了一个有效的解决方案,但我想知道是否有更好的处理方法。
问题:
我正在使用具有两个DataGridViewComboBoxColumn,col1和col2的DataGridView。
col1 已将其数据源设置为数据表。根据从 col1 中的给定单元格中选择的内容,我希望将同一行中相应的 col2 单元格的数据源设置为col2 数据源的过滤数据视图。
我当前实现的缩写代码可能更好地帮助描述我正在尝试做的事情:
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 的数据源时,如果 cell2 的当前选择未出现在新的数据视图中,则会引发异常:
System.ArgumentException: DataGridViewComboBoxCell 值无效。
为了避免这种情况,我将数据源设置为宏数据表(包含可以根据单元格 1 的选择显示的所有值),将单元格 2 的选定值更改为 DataView 中的第一个结果,然后将单元格 2 的数据源设置为表示 DataView。这一切都确保单元格永远不会有无效的选择,并且它按预期工作。
我的问题是,有没有更好/更简单的方法?供我使用,此代码仅在创建新行时激活,因此在给定形式中不会多次执行。但是,如果有更好的方法或一些改进它的建议,我将不胜感激!
(这里的第一个问题,所以我也愿意接受发布的建议......对任何失误表示歉意)
编辑(提供表格结构 - 还将"BoundID"更改为"ID1"以避免混淆):
DataGrid 的表结构为:
MainTableID int
ID1 int --这是 col1 的外键
ID2 int --这是 col2 的外键
第 1 列的表结构:
ID1 整数
显示1 瓦尔查尔(50)
第 2 列的表结构:
ID2 整型
显示2 瓦尔查尔(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);
}
}
这似乎很完美。
您的代码可以简化为这样,没有异常抛出:
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 值无效"。为了捕获此异常,请在注册的方法中注册 datagridviewDataError
没有代码的事件。然后,当您更改已设置的梳状框时,相应的组合框将填充正确的项目。
这是对我有用的解决方案的快速停止商店,但我仍然想把功劳归功于 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);
}
}
这似乎很完美。