1、System.Windows.Forms.MethodInvoker 類型是一個系統定義的委託,用於調用不帶參數的方法。
private
Thread myThread;
private
void
Form1_Load(
object
sender, EventArgs e)
{
myThread
=
new
Thread(
new
ThreadStart(RunsOnWorkerThread));
myThread.Start();
}
private
void
RunsOnWorkerThread()
{
MethodInvoker mi
=
new
MethodInvoker(SetControlsProp);
BeginInvoke(mi);
}
private
void
SetControlsProp()
{
label1.Text
=
"
myThread線程調用UI控件
"
;
}
2、直接用System.EventHandle(可帶參數)
private
Thread myThread;
private
void
Form1_Load(
object
sender, EventArgs e)
{
myThread
=
new
Thread(
new
ThreadStart(RunsOnWorkerThread));
myThread.Start();
}
private
void
RunsOnWorkerThread()
{
//
DoSomethingSlow();
string
pList
=
"
myThread線程調用UI控件
"
;
label1.BeginInvoke(
new
System.EventHandler(UpdateUI), pList);
}
//
直接用System.EventHandler,沒有必要自定義委託
private
void
UpdateUI(
object
o, System.EventArgs e)
{
//
UI線程設置label1屬性
label1.Text
=
o.ToString()
+
"
成功!
"
;
}
包裝Control.Invoke
雖然第二個方法中的代碼解決了這個問題。但它很是煩瑣,若是輔助線程但願在結束的時候提供更多的反饋信息,而不是簡單地給出
"Finieshed!"消息,則BeginInvoke過於複雜的使用方法會使人剩畏。爲了傳達其餘消息,例如「正在處理」、「一切順利」等等,
須要設法向UpdateUI函數傳遞一個參數。可能還須要添加一個進度欄以提升反饋能力。這麼屢次調用BeginInvoke可能致使輔助線程
受該代碼支配,這樣不只會形成不便,並且考慮到輔助線程與UI的協調性,這樣設計也很差。對這些分析以後,咱們認爲包裝函數可
以解決這兩個問題
private Thread myThread;
private void Form1_Load(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(RunOnWorkerThread));
myThread.Start();
}
private void RunOnWorkerThread()
{
for (int i = 0; i < 100; i++)
{
showProgress(Convert.ToString(i), i);
Thread.Sleep(100);
}
}
public void showProgress(string msg, int percentDone)
{
System.EventArgs e = new MyProgressEvents(msg, percentDone);
object[] pList = { this, e };
BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);
}
private delegate void MyProgressEventsHandler(object sender, MyProgressEvents e);
private void UpdateUI(object sender, MyProgressEvents e)
{
lblStatus.Text = e.Msg;
myProgressControl.Value = e.PercentDone;
}
public class MyProgressEvents : EventArgs
{
public string Msg;
public int PercentDone;
public MyProgressEvents(string msg, int per)
{
Msg = msg;
PercentDone = per;
}
}
ShowProgress方法對將調用引向正確線程的工做進行封裝。這意味着輔助線程代碼不用擔憂須要過多的關注UI細節,而只要按期調用
ShowProgress便可
若是我提供一個設計爲可從任何線程調用的公共方法,則徹底有可能某人會從UI線程調用這個方法。在這種狀況下,不必調用
BeginInvoke,由於我已經處於正確的線程中。調用Invoke徹底是浪費時間和資源,不如直接調用適當的方法。爲了不這個狀況,
Control類將公開一個稱爲InvokeRequired的屬性,這是「只限UI線程」規則的另外一個例外。它可從任何線程讀取,若是調用線程是
UI線程,則返回假,其餘線程則返回真。這覺得着:我能夠按如下方式修改包裝
public void ShowProgress(string msg, int percentDone)
{
if (InvokeRequired)
{
//as before
//..
}
else
{
//we're already on the UI thread just
//call straight through
UpdateUI(this, new MyProgressEventsHandler(msg, percentDone));
}
}
3、演示程序
線程中調用部分:
結束。
//
初始化參數列表
List
<
String
>
paramList
=
new
List
<
string
>
();
paramList.Add(
"
0
"
);
paramList.Add(
""
);
//
這裏採用系統委託的方式,實現線程內操做系統界面控件。
paramList[
0
]
=
"
0
"
; paramList[
1
]
=
"
清除屏幕信息
"
;
lstBoxCatchData.BeginInvoke(
new
System.EventHandler(UpdateListBox),
paramList);
///
<summary>
///
提供給系統委託事件調用,解決線程內操做界面控件的目的
///
</summary>
///
<param name="obj"></param>
///
<param name="e"></param>
private
void
UpdateListBox(
object
obj,System.EventArgs e)
{
//
強制類型轉換
List
<
String
>
paramList
=
(List
<
string
>
)obj;
if
(paramList[
0
]
==
"
0
"
)
{
this
.lstBoxCatchData.Items.Clear();
}
else
if
(paramList[
0
]
==
"
1
"
)
{
this
.lstBoxCatchData.Items.Add(paramList[
1
].ToString()); } }