3

私は次のことを行う方法を探していましたが、役に立ちませんでした。うまくいく解決策を思いつきましたが、それを処理するより良い方法があるかどうか疑問に思っています。

問題:

私は、2 つの 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 値が無効です。

それを避けるために、データソースをマクロ DataTable (cell1 の選択に基づいて表示できるすべての値を含む) に設定し、cell2 の選択した値を DataView の最初の結果に変更してから、cell2 の DataSource を設定しています。 DataView と呼ばれます。これにより、セルが無効な選択をすることがなくなり、期待どおりに機能することが保証されます。

私の質問は、これを行うためのより良い/簡単な方法はありますか? 私の使用では、このコードは新しい行を作成するときにのみアクティブになるため、指定されたフォームで数回以上実行されることはありません。しかし、それを行うためのより良い方法や改善に関するいくつかの提案があれば、私は感謝します!

(ここで最初の質問なので、投稿の提案も受け付けています...間違いについてはお詫びします)

編集 (テーブル構造を提供 - 混乱を避けるために「BoundID」を「ID1」に変更):

DataGrid のテーブル構造は次のようになります。

MainTableID int

ID1 int -- これは col1 への外部キーです。

ID2 int -- これは col2 への外部キーです。

列 1 のテーブル構造:

ID1 整数

Display1 varchar(50)

列 2 のテーブル構造:

ID2 整数

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

これは完全に機能するようです。

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

更新しました

2 番目のコンボ ボックスでフィルター処理されたデータビューが異なるため、1 番目のコンボ ボックスで既に設定されている項目を変更すると、「 DataGridViewComboBoxCell の値が無効です。ID1 」という例外がスローされます。この例外をキャッチするには、登録されたメソッドにコードを指定せずに datagridview イベントを登録します。次に、すでに設定されているコンボボックスを変更すると、対応するコンボボックスに正しいアイテムが入力されます。DataError

于 2012-10-26T12:30:28.930 に答える
0

これは私のために働いたソリューションのクイックストップショップですが、それでもモーセンの功績を認めたいと思いました. 元の質問でこれを複製しました。

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 に答える