一、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控件
"
;
}
二、直接用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));
}
}
三、演示程序
线程中调用部分:
结束。
//
初始化参数列表
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());
}
}