Thread safe???

Jul 9, 2009 at 7:17 AM

This could be something I am doing wrong, but I get a cross-thread warning when I do the following (only relevant code shown)

 

void transBW_DoWork(object sender, DoWorkEventArgs e)

{

   string transItem = string.Empty;

  //Do some work

 

   if (!String.IsNullOrEmpty(transItem)) SetMIGridItem(idx, 4, transItem);}

}

private delegate void SetMIGridItemDelegate(int row, int col, string newValue);

public void sMIGridItem(int row, int col, string newValue)

{

   if (InvokeRequired) { Invoke(new SetMIGridItemDelegate(SetMIGridItem), row, col, newValue); }

   else SetMIGridItem(row, col, newValue);

}

protected void SetMIGridItem(int row, int col, string newValue)

{

   miGrid[row, col].Value = newValue;

 

VS2005 Then throws a cross-thread exception on the InvalidateRange function of the GridVirtual:

 

public void InvalidateRange(Range range)

 {

   range = Range.Intersect(range, CompleteRange);//to ensure the range is valid

   if (range.IsEmpty() == false)

   {

      Rectangle gridRectangle = RangeToRectangle(range);

      if (gridRectangle.IsEmpty == false)

<font size="2" color="#0000ff">

      Invalidate(gridRectangle,true

</font>

);

<font size="2">

   }

}

 

I do other things with the same grid ( get the rows.count and get the value of cells, using the same delegate methodology without issues., its only on the updating/change where an exception is thrown. ) 

My preference would be for the grid to do nothing (trying not to modify source :) ) until I am done.  In otherwords, set the grid to ignore redraws by setting a property in the main UI thread, then when I am done, set the property back to allow redraws, then call refresh on the control.

The above would also be good for just regular UI calls as well.  In my main UI thread, I populate the grid with data from a DB, on a lot of data (2000'ish lines) watching the control just scroll up is just wastes resources.  Would rather say control.noRefreshing = true, fill the grid, control.noRefreshing = false;

</font>

 

Coordinator
Jul 9, 2009 at 8:39 AM

You could suspend grid, so that it would not redraw itself when it is in suspended mode.   Maybe that might help

Jul 9, 2009 at 9:18 AM

Tried doing a miGrid.SuspendLayout() in the main UI thread prior to calling the transBW.RunWorkerAsync() and got the same exception in the same location. (with miGrid.ResumeLayout(true) after the worker thread has terminated.

"System.InvalidOperationException was unhandled by user code
  Message="Cross-thread operation not valid: Control 'miGrid' accessed from a thread other than the thread it was created on."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.Control.Invalidate(Rectangle rc, Boolean invalidateChildren)
       at SourceGrid.GridVirtual.InvalidateRange(Range range) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Grids\GridVirtual.cs:line 732
       at SourceGrid.GridVirtual.InvalidateCell(Position position) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Grids\GridVirtual.cs:line 718
       at SourceGrid.Grid.InvalidateCell(Position p_Position) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Grids\Grid.cs:line 614
       at SourceGrid.Cells.Controllers.StandardBehavior.OnValueChanged(CellContext sender, EventArgs e) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Cells\Controllers\StandardBehavior.cs:line 123
       at SourceGrid.Cells.Controllers.ControllerContainer.OnValueChanged(CellContext sender, EventArgs e) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Cells\Controllers\ControllerContainer.cs:line 148
       at SourceGrid.Cells.Models.ValueModel.SetValue(CellContext cellContext, Object newValue) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Cells\Models\Real\Models.cs:line 72
       at SourceGrid.Cells.Cell.set_Value(Object value) in c:\home\darius.damalakas\projects\sourcegrid\SourceGrid4\SourceGrid\Cells\Cell.cs:line 160
       at XXX.XXX_Form.SetMIGridItem(Int32 row, Int32 col, String newValue) in F:\Projects\XXX.cs:line 286
       at XXX.XXX_Form.transBW_DoWork(Object sender, DoWorkEventArgs e) in F:\Projects\XXX.cs:line 399
       at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
       at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)"


Hopefully someone can fix, or tell me if I am doing something wrong, or if I cant sleep I will attempt to mod.

 

BTW, why the need for the log4net.dll???  I haven't dug into all the source code, but was wondering.  Personally, the fewer libs to distribute the better and since log4net is pretty common, would hate to have various versions on a box.

 

Otherwise, I appreciate the work.  I am trying to avoid using the ComponentOne grid control :)

Coordinator
Jul 9, 2009 at 3:28 PM

trunk version has log4net removed, and all other projects are merged into single dll

Jul 9, 2009 at 6:29 PM

Pulled the source from the SVN and made the following local changes (thanks for making it 1 dll)

public delegate void InvalidateDelegate(Rectangle gridRectangle, bool invalidateChildren);
  public void InvalidateRange(Range range)
  {
   if (range.IsEmpty())
    return;
   CellPositionType[] types = new CellPositionType[]{
    CellPositionType.FixedLeft,
    CellPositionType.FixedTop,
    CellPositionType.FixedTopLeft,
    CellPositionType.Scrollable
   };
   
   // invalidate eaach position type individually
   // cliping against given range
   foreach (CellPositionType type in types)
   {
    Range visibleRange = RangeAtArea(type);
    // clip our range with visible range
    // so that we wount loop through thousands of rows
    Range clippedRange = Range.Intersect(range, visibleRange);
    if (clippedRange.IsEmpty() == false)
    {
     Rectangle gridRectangle = RangeToRectangle(clippedRange);
                    if (gridRectangle.IsEmpty == false)
                        if (this.InvokeRequired) this.Invoke(new InvalidateDelegate(this.Invalidate),gridRectangle,true);
                        else this.Invalidate(gridRectangle, true);
    }
   }
  }

I now do not get any cross-threading errors and things look good so far.  However, I would suspect that all of the Invalidate calls should be modified to make similiar delegate calls just to be safe.