前言 前篇 文章主要是針對資料來源的綁定,學會了那些,那這篇就來個進階的連動
前置作業 首先在介面增加個 comboBox 並將 Name 命名為 cbDes ,介面設計如下圖
接著把上次 ComboData 類別變換一下格式,把 Value 變成 int ,記得修正因為格變換的錯誤
1 2 3 4 5 6 7 8 public class ComboData { public ComboData (string text, int value ) { Display = text; Value = value ; } public string Display { get ; set ; } public int Value { get ; set ; } }
連動 ComboBox 接著先來把剛剛新增的 comboBox 增加下拉選單的選項,並讓 Gender 的選單動態變更 Description 。先建立一個不同選項的資料對應表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private void cbDesBind (dynamic sender, string selVal ) { List<ComboData> listDes = new List<ComboData>(); switch (selVal.Trim()) { case "Male" : listDes.Add(new ComboData("Handsome" , 1 )); listDes.Add(new ComboData("Nuttiness" , 2 )); listDes.Add(new ComboData("Polite" , 3 )); listDes.Add(new ComboData("Burly" , 4 )); break ; case "Female" : listDes.Add(new ComboData("Beautiful" , 1 )); listDes.Add(new ComboData("Sexy" , 2 )); listDes.Add(new ComboData("Cute" , 3 )); break ; default : listDes.Add(new ComboData("Handsome" , 1 )); listDes.Add(new ComboData("Nuttiness" , 2 )); listDes.Add(new ComboData("Polite" , 3 )); listDes.Add(new ComboData("Burly" , 4 )); listDes.Add(new ComboData("Beautiful" , 5 )); listDes.Add(new ComboData("Sexy" , 6 )); listDes.Add(new ComboData("Cute" , 7 )); break ; } sender.DataSource = listDes; }
接著在頁面初始化的時候載入(…表示省略程式碼)
1 2 3 4 5 6 7 8 public Form1 ( ) { InitializeComponent(); ... cbDes.DisplayMember = "Display" ; cbDes.ValueMember = "Value" ; cbDesBind(cbDes, "Male" ); ... }
然後就是要讓選單連動了,在 Gender 的下拉選單觸發 SelectedIndexChanged 事件,然後呼叫剛剛的 cbDesBind
1 2 3 private void cbGender_SelectedIndexChanged (object sender, EventArgs e ) { cbDesBind(cbDes, (sender as ComboBox).Text); }
連動 DataGridView 中的下拉選單 先把預設的呈現資料增加 Description 這欄位的資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private DataTable sampleData() { using (DataTable table = new DataTable()) { ... table.Columns.Add("Des", typeof(int)); // Add rows. table.Rows.Add("Allen", 1, 0, DateTime.Now, 1); table.Rows.Add("Kevin", 1, 1, DateTime.Now, 2); table.Rows.Add("Dean", 1, 0, DateTime.Today, 3); table.Rows.Add("Jenny", 0, 1, DateTime.Today, 1); return table; } } private void gvInit() { ... DataGridViewComboBoxColumn cb = new DataGridViewComboBoxColumn() { Name = "gvDes", DataPropertyName = "Des", HeaderText = "Des", DisplayMember = "Display", ValueMember = "Value" }; cbDesBind(cb, ""); gvSample.Columns.Add(cb); ... }
這時候發現 DataGridView 的下拉選單會與新增功能的下拉選單連動,這是因為這兩個選單我偷懶使用相同資料來源,而 SelectedIndexChanged 會監看到同樣來源資料的變更, 所以這邊進行一下小調整,建一個新的資料來源
1 2 3 4 5 private void gvInit ( ) { ... gvGender.DataSource = cbData.Select(q=> q).ToList(); ... }
接著來進行 DataGridView 中的欄位連動,首先針對 DataGridView 增加一個EditingControlShowing
事件,這表示當此事件是發生於顯示編輯儲存格的控制項時
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private void gvSample_EditingControlShowing (object sender, DataGridViewEditingControlShowingEventArgs e ) { if (((DataGridView)sender).Columns[((DataGridView)sender).CurrentCell.ColumnIndex].Name == "gvGender" ) { ComboBox cb = e.Control as ComboBox; if (cb != null ) { cb.SelectionChangeCommitted += new EventHandler(cb_SelectedIndexChanged); } } } private void cb_SelectedIndexChanged (object sender, EventArgs e ) { int columnIndex = gvSample.CurrentCell.ColumnIndex; ComboBox cbx = sender as ComboBox; if (gvSample.Columns[columnIndex].Name == "gvGender" ) { string selTxt = cbx.Text, selVal = cbx.SelectedValue.ToString(); if (string .IsNullOrEmpty(selTxt) || string .IsNullOrEmpty(selVal)) return ; var targetCbx = gvSample.CurrentRow.Cells["gvDes" ] as DataGridViewComboBoxCell; cbDesBind(targetCbx, selTxt); } }
別忘記把新增的功能增加項目
1 2 3 4 5 private void button1_Click (object sender, EventArgs e ) { ... dr["Des" ] = cbDes.SelectedValue; ... }
現在剩下當範例資料載入的時候沒有去將 DataGridViewComboBox 連動,要完成這功能,只要寫個迴圈去跑 DataGridView 每 row 的資料,然後變動更新就好。把以下程式碼放到gvInit()
這方法的最後
1 2 3 4 5 6 7 8 9 10 private void gvInit ( ) { ... foreach (var dgvr in gvSample.Rows) { var targetCell = ((DataGridViewRow)dgvr).Cells["gvDes" ] as DataGridViewComboBoxCell; var genderCell = ((DataGridViewRow)dgvr).Cells["gvGender" ] as DataGridViewComboBoxCell; if (targetCell == null ) continue ; cbDesBind(targetCell, genderCell.FormattedValue.ToString()); } }
現在會發現,畫面好像還是沒有變化,這是因為呼叫gvInit()
是在 Form1 初始化的時候,在 *LifeCycle 上,有順序上的問題,所以將gvInit()
移動到Form1_Load
事件
1 2 3 private void Form1_Load (object sender, EventArgs e ) { gvInit(); }
至於為什麼不移動到 DataBindingComplete 則可以參考這篇,主要是因為會多跑一次,所以才用這種架構。
StackOverflow:Alternative to DataGridView DataBindingComplete Event link 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void Initialize_DataGridView ( ) { this .dataGridView_Items.DataContext = new Item[]{ new Item {Id = 5 , Value = 6 }}; foreach (DataGridViewRow row in grdComponents.Rows) { row.HeaderCell.Value = row.Cells[0 ].Value.ToString(); } grdComponents.Columns[0 ].Visible = false ; this .dataGridView_Items.DataContext = null ; } ... public MyForm ( ) { Initialize(); this .Initialize_DataGridView(); }
最後順便調整一下DataGridView 編輯模式,不然每次都要在 DataGridView 點擊兩次才會出現下拉選單;只要在gvInit()
中增加
1 gvSample.EditMode = DataGridViewEditMode.EditOnEnter;
下拉選單的防呆設計,增加以下程式碼在cbDesBind
方法內,放在設定 DataSource 之前就可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void cbDesBind (dynamic sender, string selVal ) { List<ComboData> listDes = new List<ComboData>(); switch (selVal.Trim()) { case "Male" : listDes.Add(new ComboData("Select..." , 0 )); ... break ; case "Female" : listDes.Add(new ComboData("Select..." , 0 )); ... break ; default : listDes.Add(new ComboData("Select..." , 0 )); ... break ; } if (sender.GetType().Name == "DataGridViewComboBoxCell" ) { var cb = (DataGridViewComboBoxCell)sender; if (!listDes.Any(q => q.Value == (int )cb.Value)) { cb.Value = 0 ; } } sender.DataSource = listDes; }
差不多完成了,但是發現這樣連動綁定,在新增一筆資料,在 Des Column 會是預設的下拉選項;如下圖所示 好吧,看起來只能使用 DataBindingComplete 作法,把gvInit()
放到Form1()
初始化,把原本的綁定程式碼移動到 DataBindingComplete 事件
1 2 3 4 5 6 7 8 9 10 11 12 private void gvSample_DataBindingComplete (object sender, DataGridViewBindingCompleteEventArgs e ) { var dgv = sender as DataGridView; if (dgv.Rows.Count > 0 && dgv.Columns.Count > 5 ) { foreach (var dgvr in dgv.Rows) { var targetCell = ((DataGridViewRow)dgvr).Cells["gvDes" ] as DataGridViewComboBoxCell; var genderCell = ((DataGridViewRow)dgvr).Cells["gvGender" ] as DataGridViewComboBoxCell; if (targetCell == null ) continue ; cbDesBind(targetCell, genderCell.FormattedValue.ToString()); } } }
畫面呈現 這個系列就先到此結束,之後有其他想法再繼續延伸吧。
VIDEO
範例程式 Github 上的 Sample Code
參考資料