2012-10-26 30 views
3

我一直在尋找一種方式來做到以下幾點,但無濟於事。我提出了一個可行的解決方案,但我想知道是否有更好的方法來處理它。動態設置DataGridViewComboBoxCell的DataSource過濾DataView基於其他單元格選擇

問題:

我使用的是有兩個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; 
    } 
} 

最後一部分是我所關注。 當動態切換小區2的數據源,如果小區2的當前選擇不會在新的數據視圖出現,那麼將引發異常:

System.ArgumentException:值的DataGridViewComboBoxCell無效。

爲了避免這種情況,我將數據源設置爲宏DataTable(包含可以基於cell1的選擇顯示的所有值),將cell2的選定值更改爲DataView中的第一個結果然後將cell2的DataSource設置爲DataView。這一切都可以確保單元格永遠不會有無效的選擇,並且按預期工作。

我的問題是,有沒有更好/更簡單的方法呢?就我的使用而言,只有在創建新行時纔會激活此代碼,因此在給定表單中這些代碼的執行時間不會超過幾次。但是,如果有更好的方法來實現它,或者提出更好的建議,我將不勝感激!

(這裏第一個問題,所以我也開放的建議張貼...對於任何失誤道歉)

EDIT(提供表結構 - 也改變了 「BoundID」 是 「ID1」,以避免混淆):

DataGrid的表結構將是:

MainTableID INT

ID1 INT - 這是一個外鍵COL1

ID2 INT - 這是一個外鍵COL2

色譜柱1的表結構:

ID1 INT

Display1 VARCHAR(50)

第2列的表結構:

ID2 INT

顯示2 VARCHAR(50)

ID1 INT - 這是一個外鍵COL1

更新時間: 我重新定義了每穆赫辛的建議的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

您的代碼可以簡化爲這個沒有異常拋出:

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事件。然後,當您更改已設置的combbox時,相應的組合框將填充正確的項目。

+0

感謝您的回覆。 當我使用上面的代碼,它似乎清除displaymember和valuemember作爲下拉結束,只是顯示** System.Data.DataRow **它返回的所有項目。 此外,如果我嘗試再次設置displaymember和valuemember,則會發生異常:**名爲Display2的字段不存在** – Smitty

+0

您能提供數據庫表的圖嗎?此外,請不要爲列設置DataPropertyName –

+0

道歉,我以前沒有指出DataGridView使用BindingSource作爲DataSource(這就是爲什麼使用DataPropertyName)的原因。 我已經根據您的要求添加了表格結構。 – Smitty

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

這似乎很好地工作。

+0

另一件要注意的事項:確保檢查確認組合單元格。我注意到如果我沒有返回並從組合框的新值中選擇一個選項,它包含舊值。 這可以通過爲RowValidating和CellValidating事件添加事件處理程序並檢查單元格的EditedFormattedValue以確保它不爲空來完成。 如果(((的DataGridViewComboBoxCell)DGV [e.ColumnIndex,e.RowIndex])EditedFormattedValue.ToString()==的String.Empty。) { dgv.Rows [e.RowIndex] .ErrorText = 「糟糕」; e.Cancel = true; } – Smitty

相關問題