先看這樣的一個例子:
點選"多線程通路"按鈕标簽中文本"此标簽被另一個線程設定文本"會變為"Hello"!
代碼是這樣寫的:
/// <summary>
/// 設定标簽的文本
/// </summary>
private void SetLableText()
{
this.label1.Text = "Hello!";
}
/// <summary>
/// 設定标簽的按鈕事件
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
System.Threading.Thread setLabelTextThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.SetLableText));
setLabelTextThread.Start();
按照想法,這個功能是完成了,運作.點選按鈕,卻出現了異常:
分析:label标簽控件是主線程建立的,不能直接從另一個線程通路.可以這樣認為:不能跨線程直接通路控件;
如何才能實作這個功能呢?
在.NET中,所有的控件都是從System.Windows.Forms.Control類派生,Control類提供了一個Invoke()方法,用于在建立控件的線程中通路線程.它的定義如下:
public Object Invoke(Delegate method);
它的參數為一個委托,代表建立控件的線程中要執行的方法.
可以利用這個方法來實作這個功能.
首先定義一個委托:
public delegate void setLabelTextDelegate();//定義一個setLabelTextDelegate()的委托
在定義一個委托變量:
private setLabelTextDelegate setLabelText;
在窗體的構造函數中給這個委托變量初始化:
public Form1()
{
InitializeComponent();
this.setLabelText = this.SetLableText;//SetLableText為上面的"設定标簽的文本"的方法
}
然後在定義一個方法.方法裡使用Invoke
private void ThreadMethod()
this.label1.Invoke(this.setLabelText);//setLabelText為上面定義的委托變量
接着把按鈕事件裡的代碼修改一下:
/// 設定标簽的按鈕事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
System.Threading.Thread setLabelTextThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.SetLableText));//這個方法修改為ThreadMethod,即:
// System.Threading.Thread setLabelTextThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.ThreadMethod));
setLabelTextThread.Start();
}
這個就OK了,運作.點選:
功能實作:
不過"設定标簽的文本"的方法SetLableText()是沒有參數的,在很多情況下,我們寫的方法都是需要參數的,下面我就把這個例子改成有參數的,并示範如何傳遞參數:
首先:改造"設定标簽的文本"的方法SetLableText()變成有參數的:
private void SetLableText(string info)
this.label1.Text = info;
既然這個方法有參數了,與它對應的委托應該使用參數:
public delegate void setLabelTextDelegate(string infor);
定義的委托變量還是在構造函數中初始化,這個不用改變什麼:
this.setLabelText = this.SetLableText;
既然使用了參數,那麼Invoke()這個方法應該會有重載的方法吧?
對Invoke()這個方法是有重載的,它的定義如下:
public Object Invoke(Delegate method,param Object [] args);
第二個參數是一個object的數組,就意味着,可以把需要傳遞的參數放到這個數組裡面來進行傳遞
對ThreadMethod()改造:
private void ThreadMethod(Object info)
this.label1.Invoke(this.setLabelText, new object[] { info});
注意紅色部分,為添加的參數
最後是按鈕事件的改造了:
System.Threading.Thread setLabelTextThread = new System.Threading.Thread(this.ThreadMethod);
setLabelTextThread.Start("Hello!");
改造成功:
跨線程通路控件步驟可以總結一下:
(1)将通路的控件代碼封裝為一個方法;
(2)根據方法自定義一個對應委托;
(3)增加一個定義的委托類型的字段,并把前面通路控件的方法"挂接"到此字段中;
(4)編寫一個線程方法,在此方法中調用要通路控件的Invoke方法,并把定義好了的委托字段做為參數傳入.
(5)在合适的地方建立線程并啟動運作