2011-07-22 68 views
0

我試圖更新背景工作程序中的Ultragridrow單元格,但當這被稱爲多於1次時,這會引發InvalidOperation異常。試圖在後臺工作線程中更改值單元格

這裏有啓動RunWorkerAsync的方法。

private void RefreshGridCacheStart() 
    { 
     try 
     { 
      if (this.uGridCache.Rows.Count == 0) 
      { 
       return; 
      } 

      if(!workerThread.IsBusy) 
      { 
       workerThread.DoWork += LookUpHostnames; 
       workerThread.ProgressChanged += UpdateCacheHostCell; 
       workerThread.RunWorkerCompleted += WorkerCompleted; 
       workerThread.WorkerReportsProgress = true; 
       workerThread.RunWorkerAsync(); 
      } 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString()); 
     } 
    } 

這是DoWork的方法:

private void LookUpHostnames(object sender, DoWorkEventArgs e) 
    { 
     var rowValues = new object[2]; 

     try 
     { 
      foreach (UltraGridRow row in uGridCache.Rows)//here is were I get an invalid operation exception 
      { 
       string cellValue = row.Cells["Host"].Text; 
       if (Globals.cNet.isValidIP(cellValue)) 
       { 
        rowValues[0] = row; 
        rowValues[1] = cellValue; 

        workerThread.ReportProgress(0, rowValues); 

        string resolvedHostname = Globals.cIPLookup.LookupHostFromIP(cellValue); 
        rowValues[1] = resolvedHostname; 

        workerThread.ReportProgress(0, rowValues); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString()); 
     } 

    } 

這是報告進展方法:

private void UpdateCacheHostCell(object sender, ProgressChangedEventArgs e) 
    { 
     var rowValues = e.UserState as object[]; 
     var row = (UltraGridRow) rowValues[0]; 
     var sMesage = (string) rowValues[1]; 

     row.Cells["Host"].Value = sMesage; 
    } 

回答

0

你可以找到答案here不同的問題,但最終同樣的問題。你正在改變foreach循環內的數據,這使得枚舉器失效。
有2個可能的解決方案我看到從閱讀你的代碼

  • 保存需要做出的變化的列表,只有foreach循環後報告一次進展情況的所有變化。儘管您在後臺處理,但這可能不是一個很好的解決方案。如果有其他代碼正在運行,也可能會更改網格中的數據,則會再次出現相同的錯誤。
  • 由於您沒有添加行,您可以輕鬆地將foreach更改爲for循環。這也可能導致一個問題,如果在主線程的代碼可能會增加,或者更糟,刪除行
+0

沒有在DoWork的方法的任何代碼被改變通過添加或刪除項目來收集行。發生此問題的可能性更大,因爲DoWork發生在另一個線程上,並且集合在別處被修改(如果這是異常的原因)。 – alhalama

+0

請參閱http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx「只要集合保持不變,枚舉數仍然有效如果對集合進行了更改(例如添加,修改或刪除元素..「更改現有項目也會使集合無效,這正是他的代碼在UpdateCacheHostCell中的作用 – Eddy

+0

代碼不會修改集合,因爲UltraGridRow對象的同一個實例將在集合之前和之後代碼被執行。您需要添加,刪除或更改集合中的項目實例,以便引發InvalidOperationException。修改列表中的對象公開的屬性的值不會導致此異常,這就是代碼所做的事情。 – alhalama

0

聽起來象是必須改變的基礎行集合,因此無效的枚舉。

如果你使用.ToList()將枚舉轉換爲列表(這將導致枚舉進行迭代並給出一個包含原始項目的新列表),您將能夠迭代這個新的枚舉並更改源代碼不會影響你。

foreach (UltraGridRow row in uGridCache.Rows.ToList()) 
{ 
    .... 
    workerThread.ReportProgress(0, rowValues); 
} 

你必須注意,如果別的東西正在改變在網格中的行,你ReportProgress可能是報告的東西,在電網不再存在的進步,你可能想在你的ReportProgress處理程序檢查在做任何事情之前,報告該項目的進展是否仍然有效。

0

關於DoWork的MSDN文檔聲明如下: 「您必須小心,不要操作DoWork事件處理程序中的任何用戶界面對象,而是通過BackgroundWorker事件與用戶界面進行通信。」

您可以查看這裏的DoWork方法的全部細節: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx

從這個事件訪問UltraGridRows導致你從另一個線程和窗口訪問的UltraGrid窗體控件不是線程安全的。

請注意,這不限於訪問該控件的屬性。如果要在UltraGrid綁定的數據源中設置值,則會出現相同的問題,因爲更改通知會在後臺線程上發生,並且仍然會從後臺線程處理UI。

注意,有一些實際上是在Windows線程安全的只有幾個成員窗體控件和這些在線程安全的控制MSDN上的部分記載:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.aspx

安全,在Windows中簡單的多線程窗體對於在Windows窗體線程一個很好的資源,即使它是舊的:

如何:使線程安全的調用到Windows窗體控件也是一個很好的資源 http://msdn.microsoft.com/en-us/library/ms171728.aspx

相關問題