动态地将DataGridViewComboBoxCell的DataSource设置为基于其他单元格选择过滤的DataView [英] Dynamically setting DataGridViewComboBoxCell's DataSource to filtered DataView based off of other cell selection

查看:216
本文介绍了动态地将DataGridViewComboBoxCell的DataSource设置为基于其他单元格选择过滤的DataView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在寻找高低的方法来做以下事情,但是无济于事。我想出了一个有效的解决方案,但是想知道是否有更好的处理方法。

I have been looking high and low for a way to do the following, but to no avail. I came up with a solution that works, but am wondering if there is a better way of handling it.

问题:

我使用的DataGridView有两个DataGridViewComboBoxColumn,col1和col2。

I am using a DataGridView that has two DataGridViewComboBoxColumn, col1 and col2.

col1已将其DataSource设置为DataTable 。基于col1中给定单元格的选择,我希望将同一行中的相应col2单元格的DataSource设置为col2的DataSource的过滤DataView。

col1 has had its DataSource set to a DataTable. Based off of the selection from a given cell in col1, I would like to have the respective col2 cell in the same row have its DataSource set to be a filtered DataView of col2's DataSource.

我目前实现的缩写代码可能会更好地帮助描述我想要做的事情:

The abbreviated code from my current implementation might better help describe what I am trying to do:

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);

然后我将事件处理程序定义为:

Then I define the event handler as:

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中,则会抛出异常:

The last part is my concern. When dynamically switching the DataSource of cell2, if cell2's current selection doesn't appear in the new DataView, then an exception will be thrown:

System.ArgumentException:DataGridViewComboBoxCell值无效

为避免这一点,我将数据源设置为宏DataTable(包含所有值可以根据cell1的选择显示),将cell2的选定值更改为DataView中的第一个结果,然后将cell2的DataSource设置为DataView。这一切都确保单元格永远不会有无效的选择,并且按预期工作。

To avoid that, I am setting the datasource to the macro-DataTable (containing all of the values that could be shown based off of cell1's selection), changing the selected value of cell2 to be the first result in the DataView and then setting cell2's DataSource to be said DataView. This all ensures that the cell is never going to have an invalid selection and it works as expected.

我的问题是,有更好/更简单的方法吗?为了使用这个代码,只有在创建新行时,才会激活此代码,因此在给定的表单中不会执行多次。但是,如果有一个更好的方法或一些建议,使它更好,我会很感激!

My question is, is there a better/simpler way of doing this? For my use, this code is only activated when creating new rows, so it isn't being done more than a few times in the given form. However, if there is a better way of doing it or some suggestions on making it better, I would appreciate it!

(第一个问题在这里,所以我也开放建议发布...对于任何失误感到抱歉)

(first question here, so I am also open to suggestions for posting ... apologies for any missteps)

编辑(提供表格结构 - 也将边界ID更改为ID1以避免混淆)

EDIT (providing table structure - also changed "BoundID" to be "ID1" to avoid confusion):

DataGrid的表格结构将是:

DataGrid's table structure would be:


MainTableID int

MainTableID int

ID1 int - 这是col1的外键

ID1 int --This is a foreign key to col1

ID2 int - 这是一个外键col2

ID2 int --This is a foreign key to col2

列1的表结构:


ID1 int

ID1 int

Display1 varchar(50)

Display1 varchar(50)

列2的表结构:


ID2 int

ID2 int

Display2 varchar(50)

Display2 varchar(50)

ID1 int - 这是col1的外键

ID1 int --This is a foreign key to col1

更新:
我已经根据Mohsen的建议重新定义了CellValueChanged事件处理程序:

Updated: I have redefined the CellValueChanged event handler per Mohsen's suggestions:

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像他建议的:

And I have added in an event handler for the DataError like he suggested:

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

这似乎很好用。

推荐答案

你的代码可以简化成这个,没有异常抛出:

Your code can be simplified to this with no exception throwing:

    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 事件。然后当您更改已设置的梳框时,相应的组合框将被填充正确的项目。

When you change an already set item in first combo box since the filtered dataview in the second combobox have different ID1, an exception is thrown with "DataGridViewComboBoxCell value is not valid.". In order to catch this exception register datagridview DataError event with no code in the registered method. Then when you change the already set combbox, the corresponding combobox will be filled with correct items.

这篇关于动态地将DataGridViewComboBoxCell的DataSource设置为基于其他单元格选择过滤的DataView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆