Select to view content in your preferred language

Error populating the fields for the selected layer in a dockpane

702
4
Jump to solution
03-17-2023 12:19 PM
tzz_12
by
New Contributor III

Hi Everyone, 

I am not sure why I am unable to populate the selected layer's fields. I have two combo boxes in my dockpane, layers in the map and selected layer fields. It's weird that when I comment out the GetFieldAsync() method, I am able to populate the feature layers on the active map, but when I don't comment the method out, then both the layer combo box and the selected layer's fields combo box does not show. If you can help me troubleshoot this problem, I would appreciate the help. Here is codes below:

   private FeatureLayer _selectedLayer;

        // Selected feature layer
        public FeatureLayer SelectedLayer
        {
            get { return _selectedLayer; }
            set
            {
                SetProperty(ref _selectedLayer, value, () => SelectedLayer);
                _ = GetFieldsAsync();

            }
        }

private async Task GetFieldsAsync()
        {
            LayerFields.Clear();
            SelectedField.Clear();
            if (SelectedLayer == null)
                return;

            await QueuedTask.Run((Action)(() =>
            {
                ObservableCollection<string> fields = new ObservableCollection<string>();

                foreach (FieldDescription fd in SelectedLayer?.GetFieldDescriptions())
                {
                    string shapeField = SelectedLayer.GetFeatureClass().GetDefinition().GetShapeField();
                    if (fd.Name == shapeField) continue;        
                    fields.Add(fd.Name);
                }

            }));
        }
0 Kudos
1 Solution

Accepted Solutions
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Hi Thi,

 I attached a 2.9 sample that should build and run in 2.8.  Remember that when you add data to the UI as a result of an Event (I.E. layer changed events) then very likely your code will run on a non-UI thread.  Also anything within QueuedTask.Run is running on a non-UI thread.  

View solution in original post

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

You are updating a list/combobox from a background thread, hence you have to take certain precautions.  Like using: 

System.Windows.Data.BindingOperations.EnableCollectionSynchronization(_layers, _lock);
            

This enables your observable collection for updates on non GUI threads.  Also once you define and 'lock' an observable collection you can't assign a new collection (like you did with your field names).    I attached a sample that shows FeatureLayers and field names on a Dockpane.  

Wolf_0-1679087390501.png

 

0 Kudos
tzz_12
by
New Contributor III

Hi Wolf,

Thanks for your help! After rereading your post, I understood what you meant. I am not able to open the example you attached. I recieved the following error message: "error : The project file cannot be opened. Unable to locate the .NET SDK. Check that it is installed and that the version specified in global.json (if any) matches the installed version." I have ArcGIS Pro SDK 2.8 installed. It looks like the SDK version does not match, most likely due to 3.x migration and SDK updates. Instead of creating a new collection, can you show me a short snippet of what I should be doing to see the layer's fields? 

Thanks, 

Thi 

0 Kudos
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Hi Thi,

 I attached a 2.9 sample that should build and run in 2.8.  Remember that when you add data to the UI as a result of an Event (I.E. layer changed events) then very likely your code will run on a non-UI thread.  Also anything within QueuedTask.Run is running on a non-UI thread.  

0 Kudos
tzz_12
by
New Contributor III

For anyone having the same issue, I made a few changes to the DockpaneWithLayerFieldSelection, but both ways work: 

 

 internal class DockpaneWithLayerFieldSelectionViewModel : DockPane
  {
    private const string _dockPaneID = "DockpaneWithLayerFieldSelection_DockpaneWithLayerFieldSelection";

    private ObservableCollection<FeatureLayer> _layers = new ObservableCollection<FeatureLayer>();
    private ObservableCollection<string> _fieldNames = new ObservableCollection<string>();
    private object _lock = new object();

    protected DockpaneWithLayerFieldSelectionViewModel() 
    {
      BindingOperations.EnableCollectionSynchronization(_layers, _lock);
      BindingOperations.EnableCollectionSynchronization(_fieldNames, _lock);
      LayersAddedEvent.Subscribe(OnLayersAdded);
      LayersRemovedEvent.Subscribe(OnLayersRemoved);
      ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChanged);
      // just in case the map is already up
      if (MapView.Active != null)
      {
        ActiveMapViewChangedEventArgs args = new ActiveMapViewChangedEventArgs(MapView.Active, null);
        OnActiveMapViewChanged(args);
      }
    }

    #region Bindings

    public ObservableCollection<FeatureLayer> Layers
    {
      get { return _layers; }
    }

    private FeatureLayer _selectedLayer;
    public FeatureLayer SelectedLayer
    {
      get { return _selectedLayer; }
      set
      {
        SetProperty(ref _selectedLayer, value, () => SelectedLayer);
        FieldNames.Clear();
        if (SelectedLayer == null) return;
        QueuedTask.Run(() =>
        {
          foreach (FieldDescription fd in SelectedLayer?.GetFieldDescriptions())
          {
            string shapeField = SelectedLayer.GetFeatureClass().GetDefinition().GetShapeField();
            if (fd.Name == shapeField) continue;
            FieldNames.Add(fd.Name);
          }
        });
      }
    }

    public ObservableCollection<string> FieldNames
    {
      get { return _fieldNames; }
    }

    private string _selectedFieldName;
    public string SelectedFieldName
    {
      get { return _selectedFieldName; }
      set
      {
        SetProperty(ref _selectedFieldName, value, () => SelectedFieldName);
        Selection = string.Empty;
        if (SelectedFieldName == null) return;
        Selection = $@"{SelectedLayer.Name}.{SelectedFieldName}";
      }
    }

    private string _Selection;
    public string Selection
    {
      get => _Selection;
      set => SetProperty(ref _Selection, value);
    }

    /// <summary>
    /// Text shown near the top of the DockPane.
    /// </summary>
    private string _heading = "Show Layer and Field Selection";
    public string Heading
    {
      get => _heading;
      set => SetProperty(ref _heading, value);
    }

    #endregion

    #region Event Handlers

    /// <summary>
    /// The active map view changed, first clear layers and fields, then
    /// refresh with new layers if incomingView is not null
    /// </summary>
    /// <param name="args"></param>
    private void OnActiveMapViewChanged(ActiveMapViewChangedEventArgs args)
    {
      FieldNames.Clear();
      Layers.Clear();
      if (args.IncomingView != null)
      {
        var layers = args.IncomingView.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();
        Layers.AddRange(layers);
      }
    }

    private void OnLayersRemoved(LayerEventsArgs args)
    {
      foreach (var layer in args.Layers.OfType<FeatureLayer>())
      {
        if (Layers.Contains(layer))
          Layers.Remove((FeatureLayer)layer);
      }
    }

    private void OnLayersAdded(LayerEventsArgs args)
    {
      foreach (var layer in args.Layers.OfType<FeatureLayer>())
      {
        Layers.Add((FeatureLayer)layer);
      }
    }

    #endregion

    /// <summary>
    /// Show the DockPane.
    /// </summary>
    internal static void Show()
    {
      DockPane pane = FrameworkApplication.DockPaneManager.Find(_dockPaneID);
      if (pane == null) return;
      pane.Activate();
    }
  }

  /// <summary>
  /// Button implementation to show the DockPane.
  /// </summary>
	internal class DockpaneWithLayerFieldSelection_ShowButton : Button
  {
    protected override void OnClick()
    {
      DockpaneWithLayerFieldSelectionViewModel.Show();
    }
  }
}