2011-07-22 36 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

相关问题