2014-01-20 70 views
-1

我已經使用後臺工作人員在傳遞過程中多次使用DoWork去獲取數據,然後使用我相信在UI線程上運行的WorkerComplete來更新UI。但是,我的工作方式,我一直在得到一個交叉線程錯誤?在RunWorkerCompleted上更新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

'Pop'應該是lowecase'pop',因爲它是一個局部變量。 – ja72

回答

1

如果你不是UI線程以外的線程中調用Rotate你應該只得到一個跨線程錯誤。 RunWorkerCompleted處理程序不會必然在UI線程上運行 - 它實際上運行在調用RunWorkerAsync時當前是SynchornizationContext。這意味着如果你在UI線程上調用Rotate,它應該發生在UI線程上。

+0

謝謝你裏德,這可能是我的問題,旋轉被另一個用戶控件在Form1中調用:((Form1)this.ParentForm).Rotate(); – Ssc456

2

RunWorkerAsync它會查看SynchronizationContext.Current的值,如果不爲空,則使用該值作爲將事件處理程序編組到UI線程的機制。它不會奇蹟般地知道如何在UI線程中運行代碼;它需要從某個地方得到一些手段。

這意味着需要從UI線程調用Rotate,因爲這是您撥打RunWorkerAsync的地方。你顯然是從非UI線程調用它。

+0

@ Ssc456你不應該有多個UI線程。如果你這樣做,你只會爲自己的世界而傷心。在所有應用程序中始終使用單個UI線程。然後它變成一個非問題。也就是說,如果你從錯誤的UI線程調用它,那麼是的,這會導致一個問題。您不能從不同於創建它的線程訪問控件;如果該線程碰巧是創建了一堆其他控件的線程,則無關緊要。 – Servy

+0

謝謝,如果我誠實的話,這似乎是在我的頭上有點。據我所知,我不(不應該)有超過1個UI線程。 Calls Rotate的UserControl是在主窗體的UI線程上創建的。然後它調用定時器的滴答事件的旋轉,據我所知應該在主UI線程上? – Ssc456

+0

@ Ssc456定時器有很多種類型,有些會在UI線程中打勾,有些則不會。 – Servy

-1

我已經定義了這些擴展方法,允許我從任何線程

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(); 
    } 
} 

調用UI元素被用作例如

progress.SafeCall(() => progress.Value=100); 

你的情況,這將是

var pop = new PopUp(); 
... 
this.SafeCall(()=> this.Controls.Add(pop)); 
+0

爲什麼downvote?請給出意見。 – ja72