我在传递中多次使用Background worker来使用DoWork来获取数据,然后使用我认为在UI线程上运行的WorkerComplete来更新UI。但是在我让它工作的那一刻我一直得到一个跨线程的错误?
继承我所拥有的:
public partial class Form1 : Form
{
Public Form1()
{
}
BackgroundWorker CheckPopUps;
DataSet Popups = new DataSet();
public void Rotate()
{
CheckPopUps = new BackgroundWorker();
CheckPopUps.DoWork += CheckPopUps_DoWork;
CheckPopUps.RunWorkerCompleted += CheckPopUps_RunWorkerCompleted;
CheckPopUps.RunWorkerAsync();
}
void CheckPopUps_DoWork(object sender, DoWorkEventArgs e)
{
DataTable Pops1 = SharedTools.DataProcedures.Popup_GetListToPop(2, ScreenName, "Interviews");
if (Pops1.Rows.Count > 1) { Popups.Tables.Add(Pops1.Copy()); }
}
void CheckPopUps_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (DataTable myTable in Popups.Tables)
{
foreach (DataRow myRow in myTable.Rows)
{
PopUp Pop = new PopUp();
Pop.SetDetails(myRow);
this.Controls.Add(Pop);
//Error occurs on this line ^
//Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.
}
}
}
}
答案 0 :(得分:2)
当RunWorkerAsync
它将查看SynchronizationContext.Current
的值时,如果不为null,则使用该值作为将事件处理程序封送到UI线程的机制。它并不神奇地知道如何在UI线程中运行代码;它需要从某个地方获得一些手段。
这意味着需要从UI线程调用Rotate
,因为那是您调用RunWorkerAsync
的地方。您显然是从非UI线程调用它。
答案 1 :(得分:1)
如果在UI线程以外的线程上调用Rotate
,则应该只会出现跨线程错误。 RunWorkerCompleted
处理程序不会必然在UI线程上运行 - 它实际上在调用SynchornizationContext
时最新的RunWorkerAsync
上运行。这意味着如果你在UI线程上调用Rotate
,它应该在UI线程上发生。
答案 2 :(得分:-1)
我定义了这些扩展方法,允许我从任何线程调用UI元素
public delegate void EmptyHandler();
public delegate void ParamHandler(params object[] args);
public static void SafeCall(this System.Windows.Forms.Control control, ParamHandler method, params object[] args)
{
if (control.InvokeRequired)
{
control.Invoke(method, args);
}
else
{
method(args);
}
}
public static void SafeCall(this System.Windows.Forms.Control control, EmptyHandler method)
{
if (control.InvokeRequired)
{
control.Invoke(method);
}
else
{
method();
}
}
例如用作
progress.SafeCall(() => progress.Value=100);
在你的情况下,它将是
var pop = new PopUp();
...
this.SafeCall( ()=> this.Controls.Add(pop) );