在RunWorkerCompleted上更新UI

时间:2014-01-20 19:51:09

标签: c# multithreading winforms

我在传递中多次使用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.
            }
        }
    }
}

3 个答案:

答案 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) );