ThinkGeo.com    |     Documentation    |     Premium Support

Background Worker and Cross Thread Exception

I am working on converting some of my 2.0 code over to 3.0 and am getting the following cross-thread exception:


-  $exception {"Cross-thread operation not valid: Control 'mapControl' accessed from a thread other than the thread it was created on."} System.Exception {System.InvalidOperationException}



This is being called from a background worker on another object and that object then raises an event that the map winforms is handling.  I have always thought that the background worker automatically marshalls the data over to the winforms control so that this would work and this did work in 2.0.  Is there something different now that would cause this not to work?


Thanks.


Curtis



Curtis,  
  
 Thanks for your post and questions! 
  
 The exception happens because you use the MapControl in another thread which is different with the thread create the MapControl, we prevent this happened by following the Microsoft .net Patten. 
  
 The way to solve this problem is that we should invoke back to the thread which create the MapControl and use it instead of using it directly in anther thread. 
  
 Of course, if you use single threaded mode for MapControl, it will have no problem. And in 2.x version, it has no second thread involved. 
  
 Any more questions just let me know. 
  
 Thanks. 
  
 Yale 


I got this exception this afternoon. I'm not sure I understand the posts above. I'm not doing any of my own threading stuff, just simply using the map with the threading mode set to Multithreaded.


I got the exception when disposing of the form with the map. Any thoughts on what I can do to get this exception to not appear?


I can post the exception if needed.


Thanks,


Kimberly



Kimberly,


Thanks for your post.
 
Basiclly my idea is that all the main form UI should be updated in the main thread or it will throw the cross-thread exception, give your following example about progressbar updating. In this example, the code is showed below.The event ShapeFileFeatureSource_BuildingIndex is exucted in another thread, while the progressbar is main thread resource, so we need to invoke back to main thread to update it.

        private void button1_Click(object sender, EventArgs e)
        {
            Thread thread = new Thread(new ThreadStart(BuildIndex));
            thread.Start();
        }

        private void BuildIndex()
        {
            ShapeFileFeatureSource.BuildIndexFile(filePath, BuildIndexMode.Rebuild);
        }

        private void ShapeFileFeatureSource_BuildingIndex(object sender, BuildingIndexShapeFileFeatureSourceEventArgs e)
        {
            progressBar1.Invoke(new MyDelegate(UpdateProgressBar), e);
        }
       
        private void UpdateProgressBar(BuildingIndexShapeFileFeatureSourceEventArgs e)
        {
            if (progressBar1.Maximum != e.RecordCount)
            {
                progressBar1.Maximum = e.RecordCount; 
            } 
            progressBar1.Value = e.CurrentRecordIndex;
        }


 
 
Besides, there is another post talking about something about it too, just have a review if you want.
gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/6139/afv/topic/Default.aspx
 
Any more questions just feel free to let me know.
 
Thanks.
 
Yale

I know I'm not MapSuite support...but I do this kind of thing all the time, with everything, not just MapSuite components.


Before you touch any UI components from a separate thread, do something like the following.  I also included an example showing parameter passing.   Note that if you do this on event callbacks you don't have to define your own delegate as there must already be a delegate you can use.


 



class MyForm : Form {
private delegate void UpdateUIDelegate();
public void UpdateUI()
{
if( InvokeRequired ) {
BeginInvoke( new UpdateUIDelegate( UpdateUI ) );
return;
}

// It's safe to touch the UI here
}

delegate void UpdateUIWithParametersDelegate( int param1, object param2 );
private void UpdateUIWithParameters( int param1, object param2 )
{
if( InvokeRequired ) {
BeginInvoke( new UpdateUIWithParameters( UpdateUIWithParameters ), new object[] { param1, param2 } );
return;
}

// It's safe to touch the UI here
}
}  


 


Chris



I appreciate the examples and Yale I looked through the other thread you referenced. However, I’m not creating my own threads at all. I’d like to utilize the built in code as much as possible before I start optimizing and adding any complexity. That said, given that I’m NEVER trying to use any other threads, it seems to me that the error is in the MapSuite Code and not something on my end.  
  
 I’d appreciate your thoughts on solving the issue. Obviously, it doesn’t come up all the time. 
  
 Thanks, 
 Kimberly

Chris, Thanks for your help. 
 Kimberly, Thanks for your post and questions.  
  
 Yes, I think this kind of Invoke back logic will be needed when some second thread instead of main thread involved even you do not create your own threads. 
  
 For example, in my example for progress bar  is updated in another thread(to keep the main thread responsive). Or we will draw the overlays in a separate thread when you set the thread mode to Multithreaded. 
  
 Of course, if you are using the SingeThreaded mode , you do not need this complexity while it will lose some responsivity. 
  
 Any more questions just feel free to let me know. 
  
 Thanks. 
  
 Yale