最近在做一個winform的小軟件(搶票的...)。登錄窗體要從遠程web頁面獲取一些數據,為了不阻塞登錄窗體的顯示,開了一個線程去加載數據遠程的數據,會報一個錯誤"線程間操作無效: 從不是創建控件“lbl_loading_msg”的線程訪問它。"百度一下,原來從.net framework 2.0開始,為了安全,不允許跨線程操作控件。解決辦法如下:
1、聲明一個擁有委托類型的方法,作為代理 操作控件的代碼:
/// <summary> /// 在線程中操作窗體的控件 /// </summary> /// <param name="action"></param> private void OpeMainFormControl(Action action) { if (this.InvokeRequired) { this.Invoke(action); //返回主線程(創建控件的線程) } else { action(); } }
2、然后在線程中,只要是對控件的操作都委托給上面的方法執行,不管是讀取還是賦值:
Thread t = new Thread(new ThreadStart(delegate() { //執行一段費時的代碼 //...... //線程中操作 控件,委托給invoker OpeMainFormControl(delegate() { this.lbl_loading_msg.Visible = false; //隱藏驗證碼加載中...的提示 this.toolStripStatusLabel1.Text = ""; //清空狀態欄 this.btnlogin.Enabled = true; //啟用登錄按鈕 }); })); t.Start(); //啟動線程
上面代碼需要注意的是:僅對控件操作的代碼委托給this.invoker。上面委托操作的方法中的this是窗體對象,也就是創建控件的線程,this.invoker(action)就是將代碼交給它來執行。這樣就操作控件的代碼就交給了創建控件的線程來執行了。我們不要將費時操作的代碼也交給this.invoker來執行,不然就失去了線程的意義了(相當於沒有開線程)。this.inoker又回到了主線程,會產生阻塞!下面代碼會阻塞:
//將費時的代碼也委托給 主線程(創建控件的線程)來執行,會阻塞窗體 OpeMainFormControl(delegate() { //執行一段費時的代碼 //...... //線程中操作 控件,委托給invoker this.lbl_loading_msg.Visible = false; //隱藏驗證碼加載中...的提示 this.toolStripStatusLabel1.Text = ""; //清空狀態欄 this.btnlogin.Enabled = true; //啟用登錄按鈕 });
還有一點:如果對控件的操作代碼沒有委托給創建它的線程來執行(比如實例化另外一個窗口,show出來),它的一些設置會不起作用,比如:窗體初始顯示位置等...
