[C#] 在code中新增物件和事件
雖然 Windows Form 很方便要什麼元件,手動拉一拉就好,但還是有機會要從 code 去新增,像下面是作業要做的樂透彩的產生器和對獎器,那麼多個數字要一個個拉動,還要設定 MouseClick 事件,非常辛苦,就想用 for loop 去產生:
大概要注意的點是要讓 Form.Controls.Add() 新增產生出來的物件給 Form,我這裡用的是 Label 和其上的 MouseClick 事件,這裡要用上 delegate,還要在滑鼠按下左鍵時做出背景與前景顏色改變,那就會牽涉到是哪個物件被按,最後是如果需要所在位置 i 的值,來與其他資料作運用,為避免界限例外的一個處理。
namespace TWLotteryExercise { public partial class Form1 : Form { Label[] lblNumber = new Label[8]; bool[] isSelected = new bool[8]; public Form1() { for (int i = 0; i < 8; i++) { isSelected[i] = false; // 用來判斷 Label 是否按下用的陣列 } for (int i = 0; i < 8; i++) { lblNumber[i] = new Label(); // Label 的設定還有 Location lblNumber[i].Text = $"{i+1}"; // Width Height 等等不贅述 lblNumber[i].BackColor = Color.White; // 最初是白底黑字 lblNumber[i].ForeColor = Color.Black; int idx = i; // 若以下事件的方法中用 i 會報錯,另外令一個 idx 就過了 lblNumber[i].MouseClick += new MouseEventHandler(delegate (object sender, MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) // 按下的是左鍵 { if(isSelected[idx]) // label 按下之前是已按過 { (sender as Label).BackColor = Color.White; // 則回到初始白底黑字 (sender as Label).ForeColor = Color.Black; isSelected[idx] = false; // 記下未按 } else // label 按下之前是未按過 { (sender as Label).BackColor = Color.Black; // 則顯示成黑底白字 (sender as Label).ForeColor = Color.White; isSelected[idx] = true; // 記下已按 } } } this.Controls.Add(lblNumSpecial[i]); // 新增到 Form 的 Controls 裡 } } } }
以上 code 有個很妙的地方,如果在事件裡面的 isSelected[idx] 改用 isSelected[i],會出現 Out of Range Exception 界限例外,偵錯說變數 i 值是 8 ,並不是我按的 Label 的 i,例如我按下寫有「5」的 Label,此物件是 lblNumber[4],理論上他的 i 是 4,那應該會看 isSelected[4] 是 true 還是 false,結果 i 變成 8 了,然後就例外跳出。
如果在事件之前另外令一個 int idx = i; 理論上他和 i 完全一樣,也是區域變數,但他就可以過關,讓對寫有「5」的 Label 按下左鍵時切換顏色。
我猜測會這樣的原因是,使用 i 的寫法在 for loop 跑到最後一個 i = 7 在結束後,步進 i++ 得 i = 8,不符合 for loop 的 condition 而結束,記憶體 i 值是 8 就這樣留著了,然後事件觸發時他又去找這個 i ,然後例外發生。
而先用一個 idx 的作法, 他確實值與 i 相同,但是 for loop 的步進 i++ 並不影響 idx 的值,他會維持在那個物件時的 i ,即使下一個 lblNumber 也是重新建立一個 int idx ,值的記憶體位置已經和上一個 lblNumber 的 idx 不同位址,所以 MouseClick 事件發生時仍然會去找之前的 idx ,指向的確實也是自己的那個 i ,而不是因為步進而改變甚至超過範圍的 i 。
留言
張貼留言