【Code】C# 元件Invoke的方法
Last Update:
Word Count:
Read Time:
前言
記錄一下C#中可以在Thread中存取Controls的方法
C# 元件執行程式
C# WindowsForm中我們可以把要執行的程式寫在元件的事件(Event)中,
例如button1按下後可以彈出MessageBox的程式為1
2
3
4
5private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello world");
//MessageBox.Show("Hello world","Here is the test", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
如果我們要把Hello world顯示在TextBox1上,那程式就會是1
2
3
4private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "Hello world";
}
需要時間的程式
現在考慮一個ConsoleApplication
要把數字 i = 0 ~ 9999一行一行地顯示在Console上,
這很簡單 :1
2
3
4for (int i = 0; i < 10000; i++)
{
Console.WriteLine(i);
}
可是如果在WindowsForm中要把 i = 0 ~ 9999一行一行地顯示在textBox1上呢 ?1
2
3
4
5for (int i = 0 ; i < 10000; i++)
{
textBox1.AppendText(i.ToString());
textBox1.AppendText(Environment.NewLine);
}
然後把這程式放到Button1內 :1
2
3
4
5
6
7
8private void button1_Click(object sender, EventArgs e)
{
for (int i = 0 ; i < 10000; i++)
{
textBox1.AppendText(i.ToString());
textBox1.AppendText(Environment.NewLine);
}
}
好像很簡單對吧 ? 但事實不是這樣,當你執行後你會發現你的WindowsForm根本動不了,過了一段時間後WindowsForm可以動,然後數字 0 ~ 9999會突然出現在textBox1上,可是現在我想要的結果是數字會像在ConsoleApplication上一行一行地顯示在TextBox上,而不是這樣突然出現,何況程式還會突然卡死。
原因
UI Thread (Main Thread)
WindowsForm在不使用其他Thread的時候在程式打開(打開.exe檔)的一刻到程式關掉時,所有事都會發生在UI Thread上,例如for loop,Thread.Sleep(),Button按鍵等等,甚至包括WindowsForm本身的上下左右移動,也就是說當你按下button1時,程式就會進入for loop,而此時for loop就是發生在UI Thread上,直到for loop結束,UI Thread才會從for loop跑出來,可是在for loop結束之前,你的for loop都無法跑出來,也就不能對WindowsForm進行住何操作,而數字顯示在textBox1上這件事也是發生在UI Thread上,但此時UI Thread還在for loop當中,所以這就是為甚麼你的程式不能移動,也不會馬上顯示數字。
解決方法
Thread
這邊我們會想到可以建立一個新的Thread,用這個Thread去執行for loop,具體方法如下 :1
2
3
4
5
6
7
8
9
10
11
12
13void RunLoop()
{
for (int i = 0 ; i < 10000; i++)
{
textBox1.AppendText(i.ToString());
textBox1.AppendText(Environment.NewLine);
}
}
private void button1_Click(object sender, EventArgs e)
{
new Thread(RunLoop).Start();
}
看上去合理的程式,執行看看,又出現問題了,IDE會直接跟你說跨執行緒作業無效。
原因
新的Thread不能直接存取另外一個Thread,需要再加上一些東西,可以使用delegate或MethodInvoke的方法解決,這邊我提供一個我自己常用的方法,就是直接對元件Invoke
對元件直接Invoke
1 | |
如此一來,數字就可以一行一行顯示
傳參數
一個參數
現在讓for loop的最大數字可控 :1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void RunLoop(object o)
{
for (int i = 0 ; i < (int)o; i++)
{
textBox1.Invoke(new Action(() =>
{
textBox1.AppendText(i.ToString());
textBox1.AppendText(Environment.NewLine);
}));
//或者以下寫法
//textBox1.Invoke(new Action(() => textBox1.AppendText(i.ToString())));
//textBox1.Invoke(new Action(() => textBox1.AppendText(Environment.NewLine)));
}
}
private void button1_Click(object sender, EventArgs e)
{
object o = 10;
new Thread(RunLoop).Start();
}
多個參數
現在讓for loop的最大數字可控且在指定TextBox顯示 :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
28void RunLoop(object o)
{
object[] obj_array = (object[])o;
int max = (int)obj_array[0];
TextBox textbox = (TextBox)obj_array[1];
//TextBox textbox = obj_array[1] as TextBox;
for (int i = 0 ; i < (int)o; i++)
{
textbox.Invoke(new Action(() =>
{
textbox.AppendText(i.ToString());
textbox.AppendText(Environment.NewLine);
}));
//或者以下寫法
//textbox.Invoke(new Action(() => textbox.AppendText(i.ToString())));
//textbox.Invoke(new Action(() => textbox.AppendText(Environment.NewLine)));
}
}
private void button1_Click(object sender, EventArgs e)
{
int max = 100;
new Thread(new ParameterizedThreadStart(RunLoop)).Start(new object[]
{
max, textBox1
});
}