3

我一直在寻找一种方法来执行以下操作,但无济于事。我想出了一个可行的解决方案,但我想知道是否有更好的方法来处理它。

问题:

我正在使用具有两个 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);
    }
}

这似乎完美无缺。

4

2 回答 2

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 值无效。 ”。为了DataError在注册方法中没有代码的情况下捕获此异常注册 datagridview 事件。那么当你改变已经设置好的组合框时,对应的组合框就会被正确的项目填充。

于 2012-10-26T12:30:28.930 回答
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);
    }
}

这似乎完美无缺。

于 2012-10-30T16:07:30.097 回答