根据其他单元格选择动态设置DataGridViewComboBoxCell的DataSource到过滤的DataView

时间:2012-10-26 00:03:14

标签: winforms datagridview c#-3.0 datagridviewcomboboxcell

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

问题:

我使用的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);
    }
}

这似乎完美无缺。

2 个答案:

答案 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);
    }
}

这似乎完美无缺。