Any idea why Map.RemoveLayers() would hang up when called?

535
13
06-11-2020 05:18 AM
MartinTyberg
New Contributor II

HI,

When I call Map.RemoveLayers() on a list of annotation layers, ArcGIS Pro hangs up. Any idea why?

Thanks,

Martin

0 Kudos
13 Replies
Wolf
by Esri Regular Contributor
Esri Regular Contributor

It's hard to tell without seeing your code.  I lifted this snippet from one of my samples and it works:

      var activeMap = MapView.Active?.Map;
      if (activeMap == null) return;

      try
      {
        var removeLayers = activeMap.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(fl => fl.Name.StartsWith("Test"));
        await QueuedTask.Run(() =>
               {
                 MapView.Active.Map.RemoveLayers(removeLayers);
               });
      }
      catch (Exception ex)
      {
        MessageBox.Show($@"Exception occurred: {ex}");
      }
0 Kudos
MartinTyberg
New Contributor II

Hi Wolfgang,

I'm doing something nearly the same. Here is my code:

Task removeOutputLayersTask = QueuedTask.Run(() =>
{
   List<Layer> layersToRemove = new List<Layer>();
   foreach (AnnotationLayer annotationLayer in m_map.GetLayersAsFlattenedList().OfType<AnnotationLayer>())
   {
      AnnotationFeatureClass featureClass = annotationLayer.GetFeatureClass() as AnnotationFeatureClass;
      using (featureClass)
      {
           if (featureClass != null)
            {
                  if (featureClass.GetDatastore().GetPath().AbsolutePath == m_geodatabaseLocation &&
                     (featureClass.GetName() == m_name)
                  {
                        layersToRemove.Add(annotationLayer);
                  }
            }
         }
   }
   m_map.RemoveLayers(layersToRemove);

}
removeOutputLayersTask.Wait(10000);

Thanks,

Martin

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Hi Martin,

 I wasn't sure about the Wait, since I wasn't patient enough I removed it, however, it works with or without.  You have to use the Datastore within a using block though.  I tried the sample code below and it worked fine ... the anno layers under the group were removed and no hang occurred. 

protected override async void OnClick()
{
  var activeMap = MapView.Active?.Map;
  if (activeMap == null) return;
  var location = @"C:/Data/FeatureTest/FeatureTest.gdb";
  var name = @"TestLinesAnno";

  await QueuedTask.Run(() =>
  {
    List<Layer> layersToRemove = new List<Layer>();
    foreach (AnnotationLayer annotationLayer in activeMap.GetLayersAsFlattenedList().OfType<AnnotationLayer>())
    {
      using (AnnotationFeatureClass featureClass = annotationLayer.GetFeatureClass() as AnnotationFeatureClass)
      {
        if (featureClass != null)
        {
          using (var dataStore = featureClass.GetDatastore())
          {
            if (dataStore.GetPath().AbsolutePath == location &&
                featureClass.GetName() == name)
            {
              layersToRemove.Add(annotationLayer);
            }
          }
        }
      }
    }
    activeMap.RemoveLayers(layersToRemove);

  });
}
0 Kudos
MartinTyberg
New Contributor II

Hi Wolfgang,

Ok, thanks. Then it's probably a bug then I guess as I'm not calling that code from a UI action (e.g. button click) but instead from a Module after the ApplicationStartupEvent event is fired and then after the OnCompletedDrawEvent is fired for the MapView I'm interested in.

Thanks,

Martin

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Looks like we are getting closer to the cause of the hang ... in general context matters because Pro will execute background operations in sequence and if you introduce an operation with a cross dependency this can lead to a 'hang'.   You can use the ArcGIS Diagnostic Monitor (which you can start once ArcGIS Pro has started by using the following key sequence: Alt+Ctrl+M) to see any such hangs.  Just as a test you can try to run your logic from the UI thread by using the following utility functions [in my case] added to a Utils class:

/// <summary>
/// utility function to enable an action to run on the UI thread (if not already)
/// </summary>
/// <param name="action">the action to execute</param>
/// <returns></returns>
internal static void RunOnUiThread(Action action)
{
    try
    {
        if (IsOnUiThread)
            action();
        else
            Application.Current.Dispatcher.Invoke(action);
    }
    catch (Exception ex)
    {
        MessageBox.Show($@"Error in RunOnUiThread: {ex.Message}");
    }
}

/// <summary>
/// Determines whether the calling thread is the thread associated with this 
/// System.Windows.Threading.Dispatcher, the UI thread.
/// 
/// If called from a View model test it always returns true.
/// </summary>
public static bool IsOnUiThread => ArcGIS.Desktop.Framework.FrameworkApplication.TestMode || System.Windows.Application.Current.Dispatcher.CheckAccess();

and using RunOnUiThread you can then run your business logic on the UI thread:

      Utils.RunOnUiThread(() =>
      {
        // add your logic here 
      });

If we take a step back though, what is it your workflow is trying to accomplish?  Are you trying to programmatically remove certain layers from the TOC when a new mapview is opened ?  Maybe there's a better way to do this.

0 Kudos
MartinTyberg
New Contributor II

Thanks for the detailed response. Unfortunately, Alt+Ctrl+M doesn't do anything once ArcGIS Pro comes up but maybe that's because its UI thread is busy in my module. But I'll look more into that Diagnostics tool.  

Basically, on start-up of ArcGIS Pro, I'm deleting an annotation table and then re-creating it in order to fill it with new annotation generated by an external batch process. If the annotation table to be deleted exists in the Table of Contents, then the Geoprocessing tool, management.Delete, hangs. In order to avoid that hang, I wanted to remove the annotation layer from the TOC since when I do this manually, then the Delete GeoProcessing tool doesn't hang. 

Thanks,

Martin

0 Kudos
MartinTyberg
New Contributor II

I determined that if I set a Timer and execute my code 10 sec after the ApplicationStartupEvent event is fired, then the map.RemoveLayer() doesn't hang so as you pointed out, ArcGIS Pro isn't ready to execute that method when the ApplicationStartupEvent is fired. Do you know if there is any event fired that indicates ArcGIS Pro is done fully starting up? 

Thanks,

Martin

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

A few questions and assumption of mine regarding the workflow.  I just want to make sure i understand your workflow before i try it:

Do you start Pro with a configuration or without any command line parameters?   How is the ArcGIS Pro project loaded - i.e. does the user click to open a project or does your add-in/configuration open it programmatically?

Here are a few assumptions of mine, let me know if these are correct:

1. You know which annotation tables you like to 'refresh' by name and file GDB path.

1. Your 'refresh' is in essence deleting the annotation feature class first and then recreating it.

1. When a mapview is opened you want it to display the 'refreshed' annotation.

And here are my assumptions of how your workflow works:

1. A mapview pane opens and draws a map including the [from above] annotation in the TOC

1. You wait for the mapview to finish drawing

1. In the OnCompletedDrawEvent  you delete the TOC entry for the annotation feature class

1. On the annotation feature class you run a Geoprocessing tool, management.Delete

1. Currently this tool hangs - but let's ignore the hang for now

1. You then recreate the a new annotation feature class using some external  process - maybe a GP tool again, this annotation feature class has the same name/path as the one you deleted before.

1. You add the annotation back to the TOC.

1. Now the mapview shows the 'refreshed' annotation class.

0 Kudos
MartinTyberg
New Contributor II

I'm starting ArcGIS Pro with command line parameters from an external driver program.  The name of the ArcGIS Pro project to load is part of the command line parameters. I've installed an add-in so that when ArcGIS Pro opens, my add-in is called. I then register in my add-in for the ApplicationStartupEvent event.  When this event is triggered, I then register for the ActivePaneChangedEvent event and DrawCompleteEvent event.  If the user saved his Project such that the currently showing pane is not a MapView that I'm interested in, I search for the pane that I am interested in the ActivePaneChangedEvent event handler and Activate it so that the MapView I"m interested in will show up and draw.

Your assumptions;

1. You know which annotation tables you like to 'refresh' by name and file GDB path. - correct

1. Your 'refresh' is in essence deleting the annotation feature class first and then recreating it. - correct

1. When a mapview is opened you want it to display the 'refreshed' annotation. - this doesn't matter to me as when the user opens his Project manually, he should see the updated annotation since the annotation layer he added manually will still be there, just with new content.

Workflow:

All correct except the last three items. I'm re-creating the annotation feature class inside the ArcGIS Pro process that I launched but, I'm doing so by copying an existing template annotation feature class that the user created because there is no way to create an annotation feature class in Pro SDK (in contrast to ArcObjects) because of a bug in the management.CreateFeatureClass GP tool that was reported in 2011 but never fixed according to the GeoNet forum). Do you happen to know if I'm wrong about that? Because the Pro docs say to use the management.CreateFeatureClass tool as the only way to create an annotation feature class but the docs for the GP tool don't have an option for feature class type of annotation (if you try with "annotation", it gives an error).

 

I don't add the annotation layer back into the TOC since when the user re-opens his Project, his annotation layer will still be there since I don't save the Project when I shut down ArcGIS Pro after the add-in workflow is completed.

Thanks,

Martin

0 Kudos