Can't access graphics layer from asynchronous operation

3297
3
04-24-2014 02:09 PM
Labels (1)
MichalKowalczuk
New Contributor
Hi, I spend whole week and still I can't find the solution. In my code, button click runs long asynchronous operation. After calculation I want add results (which type is List<Graphic>) to my GraphicsLayer.

Using ContinueWithMethod and TaskScheduler or Dispatcher works fine for all UI elements for example ProgressBar but not for my resultsGraphicsLayer. Why?

Exception: "An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code.
The calling thread cannot access this object because a different thread owns it."

private void runButton_Click(object sender, RoutedEventArgs e)
{
 var _ = RunCalcAsync();
}

private async Task RunCalcAsync()
{
 await Task.Run(() =>
 {
  \\ long time operation
  var results = RunCalc();

 }).ContinueWith((t) =>
  {
   GraphicsLayer resultsGraphicsLayer = map.Layers["results"] as GraphicsLayer;
   resultsGraphicsLayer.Graphics.AddRange(results); // this line throw an exception :-(
  },
  CancellationToken.None,
  TaskContinuationOptions.None,
  TaskScheduler.FromCurrentSynchronizationContext());
}


Any help is much appreciated. Thanks!
Mike
0 Kudos
3 Replies
MichaelBranscomb
Esri Frequent Contributor
Hi,

This is the correct behaviour. Maps and Layers must be created on the UI thread and cannot be accessed/modified on a different thread. Objects which are part of the Map/Layer such as Graphics, Renderers, and Symbols also cannot be accessed/modified on a different thread from the thread on which they were created. This includes Graphic geometries. The only exception to this in the WPF SDK is the MapPoint.MoveTo operation which we do allow through.

If you are creating new graphics on a background thread you need to push those to the UI thread then add them to the layer, or to avoid threading issues perhaps better to return your results as another data structure and create the Graphics on the UI thread (can still be done async).

Cheers

Mike
0 Kudos
MichalKowalczuk
New Contributor
But how can I do it to prevent freezing my app when it has to generate many Graphic objects in foreach-loop.

private void runButton_Click(object sender, RoutedEventArgs e)
{
 var _ = RunCalcAsync();
}

private async Task RunCalcAsync()
{
 await Task.Run(() =>
 {
  \\ long time operation
  model.RunCalc();
 });

 // creating Graphic collection
 // it works but foreach loop freeze my app
 // even if I make Task from code below

 List<Graphic> graphicCollection = new List<Graphic>();
 foreach (var item in model.Results)
 {
  Graphic graphic = new Graphic()
  {
   Geometry = PolygonFromPoint(new MapPoint(item.X, item.Y)),
  };
  graphicCollection.Add(graphic);
 }
 GraphicsLayer resultsGraphicsLayer = map.Layers["results"] as GraphicsLayer;
 resultsGraphicsLayer.Graphics.AddRange(graphicCollection);
}
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
Hi,

In the current ArcGIS Runtime SDK for WPF Graphic objects must be created on the UI thread. However, you could try creating a collection of geometries (e.g. List<Polygon>) on a separate thread and return that for the Graphic creation on the UI thread. This may provide some benefit if you are constructing polygons (the difference is negligible for MapPoints).

In the new ArcGIS Runtime SDK for .NET (currently in Beta) we have clarified the threading model so anything which is related to the Map must be created on the UI thread (i.e. Map, Layers, Renderers, Symbols) and content which is already part of a Layer such a Graphics. However, it is possible to create Graphic objects on a separate thread and then add those to a Layer on the UI thread. If your release schedule is beyond summer 2014 you should look at the new .NET SDK.

Cheers

Mike
0 Kudos