發表文章

目前顯示的是 12月, 2021的文章

[C#] 拖拉 Windows Form 的 Component

圖片
最近有需要想將 Windows Form 上的元件(例如 Label、Button)隨使用者自由拖拉,雖然事件都有 Drag 相關的可以使用,但查了一下似乎不是我想要的,這些 DragDrop()、DrapOver() 事件還有 AllowDrop 屬性好像是拖拉資料,並不是拖拉元件,只好手動用 MouseDown()、MouseMove()、MouseUp() 事件來組合。首先如下兩圖,先設置了一個 Label 叫 label1,有設定背景色和 AutoSize = false,左圖是本來的位置,右圖是用滑鼠左鍵拖拉後的結果: 這些滑鼠事件的參數 MouseEventArgs e 有 X 和 Y 兩個屬性,是 滑鼠相對於元件的坐標 ,不是相對整個表單,也不是相對整個螢幕,例如: 其實相對於什麼倒也不是這麼重要,只要得到位移向量,就能找出後來元件新的位置為何,如下圖,首先要兩個點:P 為最開始點擊 MouseDown() 事件時的定點,而 Q 為 MouseMove() 過程任何時候的動點,然後用PQ連出的向量 t,加上原來物件相對於整個表單的坐標向量 u,就得到物件新的位置的坐標向量 v: P 點就是 MouseMove() 事件中要先取到的 clickPoint,而 Q 點是 MouseMove() 事件中不停變動的 ( e.X , e.Y ),向量 t 為 ( e.X - clickPoint.X , e.Y - clickPoint.Y ),這個向量 t 再加上之前物件 Location 就得新的 Location ,也就是 ( Location.X + e.X - clickPoint.X , Location.Y + e.Y - clickPoint.Y ),參考程式碼如下: namespace DragComponentDemo { public partial class Form1 : Form { bool isDraging = false; Point clickPoint; public Form1() { InitializeComponent(); } private void label1_MouseDown(object sender, ...

[C#] 產生相異亂數

圖片
C# 有 Random 物件和 .Next() 等方法產生亂數,但不像 Python 有 random.sample() 等可直接產生相異隨機數字的好用方法,就整理了一下幾個並比較效率。以下目標都是產生 start 到 end 之間(頭尾有包含),number 個的相異且隨機的整數,並成一個整數陣列。例如 start = 20 、end = 50、number = 10,會產生 {37, 25, 48, 20, 32, 50, 44, 42, 29, 46} 作法一:單純 for loop 利用 Array 的 .Contains() 方法,若產生的新數已經出現過,就將 index-- 來重取,直到迴圈結束: public int[] RandomDistinctByForLoop(int start, int end, int number) { Random rnd = new Random(); int[] arrayResult = new int[number]; for (int i = 0; i < number; i++) { int rndResult = rnd.Next(start, end + 1); if (arrayResult.Contains(rndResult)) // 若已存在 { i--; // 使 for 不會步進,再重覆一次 } else // 若不存在 { arrayResult[i] = rndResult; // 存入亂數結果 } } return arrayResult; } 簡單好寫,缺點是如果 number 很大時,在後期會因為多數都已重覆所以耗費大量時間。 作法二:單純 while loop 和上面差不多,用 while 來思考,很容易想到的寫法,若產生的新數沒有出現過,就放入 Array,並將 index++ ,直到數量達成`: public int[] RandomDistinctByWhileLoop(int start, int...

[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 Mo...