我花了很多时间使用Windows窗体控件,但是从后台工作线程 - 我认为这是一个很好的做法,因为你不希望你的表单在人们单击按钮时锁定。说实话,我通常在后台工作线程中执行与GUI相关的所有操作,因此界面很好,可以响应用户(希望更多人会这样做!)。
所以我的问题是......每次我必须与控件交互时,我必须“调用”它们,例如:
if (control.InvokeRequired)
{
//
}
标准练习对吗?然而,这导致我一些非常混乱的代码,因为几乎每种控件类型,我需要一个MethodInvoker委托或其他东西。它为我的保护添加了数千行代码,而且非常耗费时间。
我目前有数百种“属性设置”方法,如:
private void Safe_SetLableText(Label control, string text)
{
if (control.InvokeRequired)
{
control.Invoke((MethodInvoker)delegate
{
control.Text = text;
});
}
else
{
control.Text = text;
}
}
那么,是否存在其他技术或方法,或者某种方式能够始终改变控件的属性,无论控件是什么,无论是什么线程?
类似于:(伪代码)
BackgroundWorker.RunWorkerAsync();
private void thing_to_do()
{
// We are in a background thread now
DoSomeDatabaseWorkThatTakesALongTime();
InvokeAnyControls();
// Do some stuff...
controlX.Text = "123"
controlY.Height = 300;
controlZ.text = ControlA.text;
RestoreAnyControls();
}
答案 0 :(得分:3)
您可以使用委托包装InvokeRequired
代码,如下所示:
public static void Invoke2<TControl>(this TControl c, Action<TControl> code) where TControl : Control {
if( c.InvokeRequired ) c.Invoke( delegate() { code(c); } );
else code(c);
}
然后像这样使用它:
private void Safe_SetLableText(Label control, string text) {
control.Invoke2( c => c.Text = text );
}
当然,你可能想要比Invoke2
更好的名字,但我希望这个想法能与你同在。请注意,lambda表达式语法是C#3.0特性,但Action<T>
委托是.NET 2.0的一部分,因此只要您是VS2008或更高版本,这将针对.NET Framework 2.0进行编译。 p>
答案 1 :(得分:1)
我正在回答我自己的问题,因为我认为它会为社区增加价值。
1)我想“简化”我的代码,如果最重要的发现就是那个:
control.InvokeRequired
真的不需要......它几乎是给定的。重要的是,如果您在后台(或非UI)线程中,您可以依赖于需要调用控件的事实。
2)调用将“向上”移动到控制树,所以如果你有:
表格&gt;控制&gt;控制内部控制&gt;等等&gt;等
您只需要调用“Form”(最顶层),然后就可以改变子元素的属性。
所以这是我使用后台工作者(或非UI线程)的简洁明了的解决方案。我现在刚刚对此进行了测试,效果很好。
public partial class Form1: Form
{
public Form1()
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(this.bgDoWork);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.bgComplete);
bgw.RunWorkerAsync();
}
private void bgComplete(object sender, EventArgs e)
{
// You are not in the UI thread now, so you can Invoke without error
this.Invoke((MethodInvoker)delegate
{
// Now you can change any property an any control within this form.
// Remember "this" refers to Form1.
this.label1.Text = "test123";
this.label2.Text = "test456";
this.label3.Text = this.label4.Text;
// You can set progress bars too, not just label text
}
}
private void bgDoWork(object sender, DoWorkEventArgs e)
{
// Do something that takes a long time
}
}
答案 2 :(得分:0)
由于您已经在使用背景工作人员,为什么不要滥用&#39; OnProgressChanged?
private void thing_to_do()
{
// We are in a background thread now
DoSomeDatabaseWorkThatTakesALongTime();
BackgroundWorker.ReportProgress(1, "state");
DoSomeMoreDatabaseWorkThatTakesALongTime();
BackgroundWorker.ReportProgress(2, YourObjectHere);
}
void OnProgressChanged(ProgressChangedEventArgs progressArgs)
{
switch(progressArgs.ProgressPercentage)
{
case 1:
// Do some stuff...
controlX.Text = "123"
controlY.Height = 300;
controlZ.text = ControlA.text;
break;
case 2:
// other stuff
YourObject obj = (YourObject) progressArgs.UserState;
// wahtever...
break;
default:
break;
}
}