一、問題描述
進行winform 開發我們在進行數據交換時避免不了使用多線程或異步方法,這樣操作也將避免不了跨線程對控件進行操作(賦值、修改屬性)。
下面通過一個測試說明一下問題
點擊一個按鈕異步對textbox進行賦值
運行測試結果

1 private void button1_Click(object sender, EventArgs e) 2 { 3 Action<string> action = new Action<string>((str) => 4 { 5 // 解決跨線程賦值 6 this.textBox1.Text = str; 7 }); 8 action.BeginInvoke("Test", null, null); 9 }
產生錯誤的原因:textBox1是由主線程創建的,異步方法(或子線程)是另外創建的一個線程,在.NET上執行的是托管代碼,C#強制要求這些代碼必須是線程安全的,即不允許跨線程訪問Windows窗體的控件。
二、解決方法
1.在窗體的加載事件中,將C#內置控件(Control)類的CheckForIllegalCrossThreadCalls屬性設置為false,屏蔽掉C#編譯器對跨線程調用的檢查。

1 //取消跨線程的訪問 2 Control.CheckForIllegalCrossThreadCalls = false;
使用上述的方法雖然可以保證程序正常運行並實現應用的功能,但是在實際的軟件開發中,做如此設置是不安全的(不符合.NET的安全規范),在產品軟件的開發中,此類情況是不允許的。如果要在遵守.NET安全標准的前提下,實現從一個線程成功地訪問另一個線程創建的空間,要使用C#的方法回調機制。
2.使用回調函數 ( 委托的應用 )
(1) 定義聲明回調
(2) 初始化回調
(3) 觸發動作

1 /// <summary> 2 /// 定義委托 3 /// </summary> 4 /// <param name="str"></param> 5 private delegate void SetValueDelegate(string str); 6 7 /// <summary> 8 /// 聲明委托 9 /// </summary> 10 SetValueDelegate test; 11 12 /// <summary> 13 /// 回調方法 14 /// </summary> 15 /// <param name="str"></param> 16 private void SetTbValue(string str) 17 { 18 this.textBox1.Text = str; 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 // 實例化委托 24 test = new SetValueDelegate(SetTbValue); 25 26 Action<string> action = new Action<string>((str) => 27 { 28 // 解決跨線程賦值 29 this.textBox1.Invoke(test, str); 30 }); 31 32 action.BeginInvoke("Test", null, null); 33 }
我個人比較使用喜歡使用事件解決(原理同委托一樣),但省掉不少代碼

1 private void button1_Click(object sender, EventArgs e) 2 { 3 Action<string> action = new Action<string>((str) => 4 { 5 // 解決跨線程賦值 6 this.textBox1.Invoke( 7 new Action<string>((param) => 8 { 9 this.textBox1.Text = param; 10 } 11 ), str); 12 }); 13 14 action.BeginInvoke("Test", null, null); 15 }