UI消息响应、事件通知、界面刷新,都是通过循环在对话框被调用线程中执行的。
UI新手容易在主线程中实现一些耗时计算,然后让界面卡死在那,非常不友好。
UI中手会迂回一下,比如C#中的delegate调用刷新进度条的经典案例。然后照葫芦画瓢每个控件都搞个delegate,非常不优雅。
例如之前这篇博文里总结的方法:https://blog.csdn.net/lonelyrains/article/details/90712993
下面示例演示了C#的winform中,主界面线程中新建两个子线程。第一个线程显示一个子界面,第二个线程刷新第一个线程中的子界面的子控件。将UI调用通过统一的delegate然后反射到对应的控件上真正执行。
需要注意的是,默认的控件都是private的, 这里如果要能通过外部调用访问控件的名称,最简单的方法是改成public声明。
// 从主对话框的子线程调用其他线程中显示的界面的子控件的刷新
private void buttonTest_Click(object sender, EventArgs e)
{
new Thread(() => form2.ShowDialog()).Start();
new Thread(() => testProc()).Start();
}
private void testProc()
{
for (int i = 0; i < 101; i++)
{
Thread.Sleep(200);
form2.updateUI(form2.progressBar1.Name, "", i);
form2.updateUI(form2.textBox1.Name, "test:" + i, i);
form2.updateUI(form2.labelTime.Name, DateTime.Now.ToString("yyyy-MM-dd hh:mm"), i);
}
}
// 集中反射实现控件解析和刷新
delegate void UIDelegate(string controlName, string text, int value = 0, string method = "");
public void updateUI(string controlName, string text, int value = 0, string method = "")
{
UIDelegate uidelegate = new UIDelegate(updateUI);
foreach (FieldInfo f in this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public))
{
if (f.Name == controlName)
{
Control control = ((Control)f.GetValue(this));
if (control.InvokeRequired)
{
this.Invoke(uidelegate, new object[] { controlName, text, value, method });
}
else
{
switch(control.GetType().Name)
{
case "ProgressBar":
{
((ProgressBar)control).Value = value;
}
break;
case "Label":
case "TextBox":
{
control.Text = text;
}
break;
default:
break;
}
}
break;
}
}
}