私は次のことを行う方法を探していましたが、役に立ちませんでした。うまくいく解決策を思いつきましたが、それを処理するより良い方法があるかどうか疑問に思っています。
問題:
私は、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);
}
}
これは完全に機能するようです。