ProConcepts Map Authoring - kataya/arcgis-pro-sdk GitHub Wiki

The Mapping functionality in ArcGIS Pro is delivered through the ArcGIS.Desktop.Mapping assembly. The ArcGIS.Desktop.Mapping namespace provides classes and members to author maps. This includes creating maps, opening web maps in ArcGIS Online, adding content—such as layers—to maps, creating and editing symbols, assigning renderers to layers, and supporting map annotation and dynamic labeling. The ArcGIS.Desktop.Mapping namespace also provides the ability to manage styles and style items in an ArcGIS Pro project.

  • ArcGIS.Desktop.Core.dll
  • ArcGIS.Desktop.Mapping.dll
Language:      C#
Subject:       Map Authoring
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          11/24/2020
ArcGIS Pro:    2.7
Visual Studio: 2017, 2019

In this topic

Creating and displaying maps

The Map object is the primary object used for the organization of geographic data in ArcGIS Pro. It is a container of layers and stand-alone tables. Typical tasks with a map object include adding a new layer, changing the spatial reference, obtaining the currently selected features, and managing spatiotemporal bookmarks. To view and interact with maps or features, use the MapView object.

You can use the Map object to access the map and the data it contains. The Map object is a primary point for customization tasks because it not only manages layers of data, but it is also a view and has to manage the drawing of its data. Typical tasks with the Map object include adding a new layer, setting a basemap, changing the spatial reference, and obtaining the currently selected features and elements.

The Map object has properties that operate on all layers within the map, such as spatial reference, map scale, and so on, along with methods that manipulate the map's layers. Layers can, if required, handle drawing operations for their associated data, but it is more common for layers to have an associated renderer object. The properties of the renderer object control how the data is displayed in the map. Renderers use symbols for the actual drawing—the renderer matches a particular symbol with the properties of the entity that is to be drawn.

Because of the asynchronize nature of the application, a Map object cannot be created using its constructor; in fact, the Map class does not have a public constructor. To create a map object, you need to use methods from MapFactory.Instance. Methods such as MapFactory.Instance.CreateMap() add the newly created map into the current project. You do not need to write additional code for adding a map to a project.

// create a new map with an ArcGIS Online basemap
Map map = MapFactory.Instance.CreateMap("World Map", ArcGIS.Core.CIM.MapType.Map, ArcGIS.Core.CIM.MapViewingMode.Map, Basemap.Streets);
// open an existing map 
var mapProjectItems = Project.Current.GetItems<MapProjectItem>();
var mapProjectItem = mapProjectItems.FirstOrDefault(mpi => mpi.Name.Equals("World Map"));
Map map = mapProjectItem.GetMap();
ProApp.Panes.CreateMapPaneAsync(map);
// access the currently active map
Map map = MapView.Active.Map;
// set or change the basemap layer of the active map
Map map = MapView.Active.Map;
map.SetBasemapLayers(Basemap.Gray);

Working with web maps

ArcGIS Pro supports seamless integration with ArcGIS Online or your organization portal. You can add and open an existing web map in your project, add an existing portal layer item to a map, or publish a map from your project as a web map to share within your organization or with a wider audience.

Create basemaps

A basemap is a non-editable layer that provides background, or reference information, in your map. It is typically designed to provide a visual reference for other layers to help orient the user of the map. Aerial imagery, terrain, and streets from ArcGIS Online are examples of basemap layers you can add to your map. In addition, you can create custom basemap layers for your project by creating a map of basemap type and authoring it as if it were a regular map. The map appears in the basemap layer gallery on the ribbon as well as in the Content pane with a different icon.

// create custom basemap in your current project
Map basemap = MapFactory.Instance.CreateMap("My basemap", MapType.Basemap);
// create and add layer to the basemap using LayerFactory.Instance
string url = @"c:\temp\roads.shp"; 
Uri uri = new Uri(url);
Layer lyr = LayerFactory.Instance.CreateLayer(uri, basemap);

Map Metadata

Map implements the ArcGIS.Desktop.Core.IMetadataInfo to provide read and write access to its metadata*. Map metadata is saved with the map in the .aprx. Metadata retrieved from the map will be in xml format and will be styled according to whatever is the current metadata style in use. It is the add-in's responsibility to ensure that what the rules associated with the current metadata style are met when editing metadata.

 //Supports access and update of metadata
 public interface IMetadataInfo {
   // Returns true if the metadata can be edited otherwise false
   bool GetCanEditMetadata();
  // Returns the xml formatted metadata
  string GetMetadata();
 //Set the metadata xml. The requirements of the current style
 //should be met
 void SetMetadata(string metadataXml);
}

In this example, the map metadata is retrieved and "set" without any changes if the map metadata is editable. As a minimum, for map metadata to be editable, the project access permissions must be read/write.

 var map = MapView.Active.Map; //Assume null check...

 QueuedTask.Run(() => {
   //read the map metadata and set it back if the metadata
   //is editable
   var map_xml = map.GetMetadata();
   if (map.GetCanEditMetadata())
      map.SetMetadata(map_xml);
 });

* Metadata access is also available off the ArcGIS.Desktop.Mapping.MapProjectItem

Working with MapMembers

MapMember is an abstract class and represents items contained within a map. It contains common methods and properties that are shared by layers and stand-alone tables. The Layer and StandaloneTable classes inherit from the MapMember class. To get a list of available layers and stand-alone tables in a map, use the Map.Layers and Map.StandaloneTables properties. The result from Map.Layers maintains the group layer structure. Should you need to get a list without group layers hierarchy, use Map.GetLayersAsFlattenedList() method.

//Generic MapMember class hierarchy

                                                            MapMember
                                          |--------------------|-----------------|
                                          |                                      |
                                        Layer                               StandaloneTable
                                          |
                |-------------------------|-----------------------------------------|
                |                         |                                         |
         BasicFeatureLayer          BasicRasterLayer                          CompositeLayer
                |                         |                                         |
      |---------|---------|          |----|----------|                 |------------|---------|
      |                   |          |               |                 |            |         |
AnnotationLayer    FeatureLayer  RasterLayer  ImageServiceLayer    GroupLayer  MosaicLayer  (etc.)

To find an existing MapMember such as a layer or a stand-alone table, use the FindLayer or FindStandaloneTable method respectively from a map. These methods allow you to search by unique layer or table URI. The FindLayer method is a member of the ILayerContainer interface, which is implemented by GroupLayer and CompositeLayer, so you can find a layer from a map or a composite layer. Use the 'FindLayers' or 'FindStandaloneTables' methods to find layers or standalone tables by name.

These functions do not support partial searches, but you can achieve that by using the .NET lambda expression.

//find all layers from the active map that contain 'world' in their name
Map map = MapView.Active.Map;
IEnumerable<Layer> layers = map.GetLayersAsFlattenedList().Where(l => l.Name.Contains("world"));

To get the selected layers or tables, you need to use MapView class, not Map.

MapView mapView = MapView.Active;
IReadOnlyList<Layer> selectedLayers = mapView.GetSelectedLayers();
IReadOnlyList<StandaloneTable> selectedTables = mapView.GetSelectedStandaloneTables();

MapMember metadata

Layers and standalone tables implement the ArcGIS.Desktop.Core.IMetadataInfo to provide read and write access to their metadata*. By default, new map layers and tables created by adding a data source to the map reference will utilize the metadata associated with the underlying data source. For example, when a new feature layer is added to a map, the feature layer will use the feature class metadata by default. Layer and table metadata can be switched between their own (independent) full metadata and (dependent) data source metadata via the IMetadataSource interface. MapMember metadata being accessed off the source is read-only and mapMember.GetCanEditMetadata() will return false.

MapMember metadata is saved with the map member in the .aprx. Metadata will be in xml format and will be styled according to whatever is the current metadata style in use. It is the add-in's responsibility to ensure that what the rules associated with the current metadata style are met when editing metadata.

 //Supports access and update of metadata
 public interface IMetadataInfo {
   // Returns true if the metadata can be edited otherwise false
   bool GetCanEditMetadata();
   // Returns the xml formatted metadata
   string GetMetadata();
   //Set the metadata xml. The requirements of the current style
   //should be met
   void SetMetadata(string metadataXml);
 }

 //Indicates if metadata can be retrieved from a source other than itself
 public interface IMetadataSource {
   //Gets whether an underlying source is being used for metadata
   bool GetUseSourceMetadata();
   // Set to true to use the underlying source object's metadata
   void SetUseSourceMetadata(bool useSource);
 }

In this example, layer metadata is set to be independent of its source.

  //var layer = ...;
  //Must be on the QueuedTask.Run()

  //Is this layer using source metadata?
  if (layer.GetUseSourceMetadata())
    //Set the metadata to be independent of the underlying layer source
    layer.SetUseSourceMetadata(false);
  

In this example, a layer's metadata is retrieved and "set" without any changes if the layer metadata is editable. As a minimum, for layer (and table) metadata to be editable, mapMember.GetUseSourceMetadata() must be false and project access permissions must be read/write.

 //var layer = ...;

 QueuedTask.Run(() => {
   //read the layer metadata and set it back if the metadata
   //is editable
   var layer_xml = layer.GetMetadata();
   if (layer.GetCanEditMetadata())
      layer.SetMetadata(layer_xml);
 });

* _Sub_layers do not support metadata. Check the layer.SupportsMetadata property for true|false depending on whether metadata is supported or not. Sublayers include: AnnotationSubLayer, ServiceCompositeSubLayer, ServiceSubLayer, and WMSSubLayer. Attempting to access metadata off a sublayer (where SupportsMetadata is false) will throw a NotSupportedException.

Working with layers

Layers display geographic information on a map. A layer does not store the actual geographic data; rather, it references the data contained in shapefiles, geodatabases, images, and so on, then defines how to display this geographic data. Most often, a layer references to a single shapefile, table, or a view in the database. Sometimes the source of a layer is a joined table made of two or more tables. Some layers do not refer to geographic data. For example, a GroupLayer refers to other layers, and an AnnotationLayer stores text or graphic elements.

Each type of layer object represents different types of data. Examples of layer objects include the following:

  • FeatureLayer
  • RasterLayer
  • GroupLayer

All layers inherit from an abstract class called Layer, which inherits from MapMember. Layers do not have a public constructor; you cannot create them with a new keyword. Instead, you need to use one of the methods from LayerFactory.Instance to create one. The LayerFactory.Instance provides methods, such as CreateLayer(), to create any layer and returns an object of Layer type or a method such as CreateFeatureLayer() or CreateGroupLayer() to create specific layers such as FeatureLayer or GroupLayer respectively.

Layer create methods allow you to use the following:

  • Local or shared paths to shapefiles, FeatureClasses, and so on
  • URLs to map or feature services
  • Item objects

A layer must be part of a LayerContainer. Therefore you need to pass in a LayerContainer (for example, a map or a group layer that implements the ILayerContainer interface) to the create method.

//create a layer from a shapefile
string uriShp = @"\\Machine\SharedFolder\Census.shp";
Layer lyr = LayerFactory.Instance.CreateLayer(new Uri(uriShp), map);

//create a layer from a feature class off an sde
string uriSde = @"c:\MyDataConnections\MySDE.sde\Census";
Layer lyr = LayerFactory.Instance.CreateLayer(new Uri(uriSde), map);

//create a layer using a URL
string url = @"http://sampleserver6.arcgisonline.com/arcgis/rest/services/NapervilleShelters/FeatureServer/0";
Layer lyr = LayerFactory.Instance.CreateLayer(new Uri(url), map);

// create a layer and add it to a groupLayer
string urlLyrx = @"\\Machine\SharedFolder\Census.lyrx";
Layer lyr = LayerFactory.Instance.CreateLayer(new Uri(urlLyrx), grpLayer);

When creating a layer, there may be certain parameters you wish to set prior to adding it to the map. The LayerCreationParams class was created with this purpose. For example you may wish to add a layer to a map but have it invisible by default.

var uri = new Uri(@"c:\MyDataConnections\MySDE.sde\LANDUSE_polygon");
var createParams = new LayerCreationParams(uri)
{
   Name = "Landuse",
   IsVisible = false,
};

var layer = LayerFactory.Instance.CreateLayer<Layer>(createParams, MapView.Active.Map);

Note the use of the Template <Layer> in the above CreateLayer call.

Map Notes can be added to the map. Map Notes are available as a collection of layer template packages. Each of these Map Notes can be addded to the map as an "item" object using the LayerCreationParams.

//Gets the collection of layer template packages installed with Pro for use with maps
var items = MapView.Active.Map.LayerTemplatePackages;     
//Iterate through the collection of items to add each Map Note to the active map
foreach (var item in items)
{
  //Create a parameter item for the map note
  var layer_params = new LayerCreationParams(item);
  layer_params.IsVisible = false;
  await QueuedTask.Run(() => {
    //Create a feature layer for the map note
    var layer = LayerFactory.Instance.CreateLayer<Layer>(layer_params, MapView.Active.Map);
  });
}

Working with feature layers

A FeatureLayer is a layer that is based on vector geographic data, which is typically a geodatabase, shapefile feature class, or sublayer off a map or feature service. It allows you to draw and/or edit the underlying features. A DefinitionQuery can be used to draw a subset of all features.

//create a feature layer to ONLY cities in California
FeatureLayer flyr = LayerFactory.Instance.CreateFeatureLayer(new Uri(strUri), map);
flyr.SetDefinitionQuery("state_name = 'California'");

LayerFactory.Instance.CreateFeatureLayer() allows you to pass in a RendererDefinition to create a feature layer with the desired symbology instead of with the default symbology.

//create a feature layer with a graduated color renderer on a numeric field
RendererDefinition rd = new GraduatedColorsRendererDefinition("POP10_SQMI");
FeatureLayer flyr = LayerFactory.Instance.CreateFeatureLayer(uri, myMap, layerName: "Population Density", rendererDefinition: rd);

You can also accomplish the same using the CreateLayer call with the FeatureLayerCreationParams class.

RendererDefinition rd = new GraduatedColorsRendererDefinition("POP10_SQMI");

var createParams = new FeatureLayerCreationParams(uri)
{
   Name = "Population Density",
   RendererDefinition = rd
};

FeatureLayer flyr = LayerFactory.Instance.CreateLayer<FeatureLayer>(createParams, myMap);

The FeatureLayerCreationParams class also allows you to set a DefinitionFilter on the layer.

RendererDefinition rd = new GraduatedColorsRendererDefinition("POP10_SQMI");

var createParams = new FeatureLayerCreationParams(uri)
{
   Name = "Population Density",
   RendererDefinition = rd,
   DefinitionFilter = new CIMDefinitionFilter()
   {
     DefinitionExpression = "POP > 1000",
     Name = "Population greater than 1000"
   }
};

FeatureLayer flyr = LayerFactory.Instance.CreateLayer<FeatureLayer>(createParams, myMap);

Renderers

Renderers are objects that store symbolization for feature layers and draw this data based on the stored symbolization rules. To create a renderer in ArcGIS Pro, it is recommended that you create a RendererDefinition and delegate the task to a feature layer by calling the CreateRenderer method. You get a CIMRenderer object. If you need to, you can modify that and assign it to the feature layer by calling the SetRenderer method. You can get hold of the current renderer assigned to a feature layer—for example, if you want to modify a symbol used in the renderer—by calling the GetRenderer method.

//assign a graduated color renderer with 6 breaks and exclusion clause
GraduatedColorsRendererDefinition gcDef = new GraduatedColorsRendererDefinition()
{
  ClassificationField = "CROP_ACR07",
  ClassificationMethod = ArcGIS.Core.CIM.ClassificationMethod.NaturalBreaks,
  BreakCount = 6,
  ExclusionClause = "CROP_ACR07 = -99",
  ExclusionSymbol = aExclusionSymbol,
  ColorRamp = aColorRamp,
  SymbolTemplate = aSymbolTemplate,
};

CIMRenderer gcRenderer = aFeatureLayer.CreateRenderer(gcDef);
aFeatureLayer.SetRenderer(gcRenderer);

There are more examples of Renderers in the Map Authoring snippets

Labeling

The labeling properties of a layer are stored in the LabelClasses collection available on FeatureLayer. To update or read properties on an existing LabelClass, get the LabelClass you want from the collection. The LabelClass provides access to commonly updated label class properties. Full label placement properties can be accessed for the label engine the map is currently using by calling GetMaplexLabelPlacementProperties() or GetStandardLabelPlacementProperties(), which return CIMMaplexLabelPlacementProperties and CIMStandardLabelPlacementProperties, respectively. To update these properties, call SetMaplexLabelPlacementProperties() or SetStandardLabelPlacementProperties() depending on the label engine being used. Call GetLabelEngine on the Map to determine which label engine is currently being used. Map level labeling properties can be accessed via the Map method GetGeneralPlacementProperties() and updated via SetGeneralPlacementProperties().

Working with raster layers

RasterLayer represents an image or pixel data on disk or in a geodatabase.

// Create a RasterLayer from an image on disk.
string url = @"C:\Images\Italy.tif";
RasterLayer rasterLayer = (RasterLayer)LayerFactory.Instance.CreateLayer(new Uri(url), map);

LayerFactory.Instance.CreateRasterLayer() allows you to pass in RasterColorizerDefinition to create a raster or image service layer with the desired colorizer instead of with the default colorizer.

// Create a new stretch colorizer definition using default constructor.
StretchColorizerDefinition stretchColorizerDef = new StretchColorizerDefinition();
await QueuedTask.Run(() =>
{
  // Create a raster layer using the colorizer definition created above.
  // Note: You can create a raster layer from a url, project item, or data connection.
  RasterLayer rasterLayerfromURL =
    LayerFactory.Instance.CreateRasterLayer(new Uri(url), aMap, 0, layerName, stretchColorizerDef) as RasterLayer;
});

Working with mosaic layers

MosaicLayer is a group layer that represents a mosaic dataset. A mosaic dataset is a collection of images that have been seamlessly mosaicked together so that they look like one image. The mosaic layer group layer can contain up to four different sublayers:

  • Boundary—Feature layer that represents the boundary of the mosaic.
  • Footprint—Feature layer that represents the footprints of the images comprising the mosaic.
  • Seamline—Feature layer that represents the seamlines between the images of the mosaic. This layer only shows if seamlines have been generated.
  • Image—Image service layer that represents the mosaic. Users can control the way images are displayed, mosaicked, and transmitted through this layer.

Feature layers that are part of a mosaic layer can be distinguished from other feature layers by using the FeatureMosaicSubLayer class. Similarly, an image service layer that is part of a mosaic layer can be distinguished from other image service layers by using the ImageMosaicSubLayer class.

// Get the Image sublayer of the mosaic layer.
ImageMosaicSubLayer mosaicImageSublayer = mosaiclayer.GetImageLayer();
// Get the Footprint sublayer of the mosaic layer.
FeatureMosaicSubLayer mosaicFootprintSubLayer = mosaiclayer.GetFootprintLayer();
// Get the Boundary sublayer of the mosaic layer.
FeatureMosaicSubLayer mosaicBoundarySubLayer = mosaiclayer.GetBoundaryLayer();
// Get the Seamline sublayer of the mosaic layer.
FeatureMosaicSubLayer mosaicSeamlineSubLayer = mosaiclayer.GetSeamlineLayer();

LayerFactory.Instance.CreateMosaicLayer() allows you to pass in RasterColorizerDefinition to create a mosaic layer with the desired colorizer instead of with the default colorizer.

// Create a new colorizer definition using default constructor.
StretchColorizerDefinition stretchColorizerDef = new StretchColorizerDefinition();
await QueuedTask.Run(() =>
{
  // Create a mosaic layer using the colorizer definition created above.
  // Note: You can create a mosaic layer from a url, project item, or data connection.
  MosaicLayer newMosaicLayer =
    LayerFactory.Instance.CreateMosaicLayer(new Uri(url), aMap, 0, layerName, stretchColorizerDef) as MosaicLayer;
});

Working with image service layers

ImageServiceLayer represents pixel data coming from an image service or a mosaic dataset. Image service layers allow users to change the order in which images are mosaicked together (called the mosaic method) and how the overlapping portions of the images are displayed (called the mosaic operator). The image service layer also allows users to control the compression of the image being transmitted.

// Get the mosaic rule of the image service.
CIMMosaicRule mosaicRule = isLayer.GetMosaicRule();
// Set the mosaic method to be Center.
mosaicRule.MosaicMethod = RasterMosaicMethod.Center;
// Update the image service with the changed mosaic rule.
isLayer.SetMosaicRule(mosaicRule);

LayerFactory.Instance.CreateRasterLayer() allows you to pass in RasterColorizerDefinition to create a raster or image service layer with the desired colorizer instead of with the default colorizer.

// Create a new colorizer definition using default constructor.
StretchColorizerDefinition stretchColorizerDef = new StretchColorizerDefinition();
await QueuedTask.Run(() =>
{
  // Create an image service layer using the colorizer definition created above.
  ImageServiceLayer imageServiceLayer =
    LayerFactory.Instance.CreateRasterLayer(new Uri(url), aMap, 0, layerName, stretchColorizerDef) as ImageServiceLayer;
});

Colorizers

Raster, mosaic, and image service layers are drawn based on properties stored in colorizers. Use the GetColorizer method on the raster and image service layers to get a CIMRasterColorizer object and make modifications accordingly. Use the SetColorizer method to apply the changes.

To create colorizers in ArcGIS Pro, it is recommended that you create a RasterColorizerDefinition first and then delegate the task of creating the colorizer to a raster, mosaic or image service layer by calling the CreateColorizer method.

// Get the colorizer from the raster layer.
CIMRasterColorizer rasterColorizer = rasterLayer.GetColorizer();
// Update raster colorizer properties.
rasterColorizer.Brightness = 10;
rasterColorizer.Contrast = -5;
rasterColorizer.ResamplingType = RasterResamplingType.NearestNeighbor;
// Update the raster layer with the changed colorizer.
rasterLayer.SetColorizer(rasterColorizer);

Layer Files and Layer Packages

A layer can exist outside of your map or project as a layer file (.lyrx). A layer file is a file that stores the path to a source dataset and other layer properties including symbology. This makes it easy for others to access the layers you've built. When you add a layer file to a map, it draws exactly as it was saved provided the data referenced by the layer file is accessible.

A layer can also be saved with its data as a layer package (.lpkx). A layer package includes both the layer properties and the dataset referenced by the layer. With a layer package, you can save and share everything about the layer — its symbolization, labeling, table properties, AND the data. Other users will be able to add layer packages directly into their maps without having to know how to access the database or classify the data.

Any type of layer can be saved as a layer file or layer package, including group layers. Remember, a layer file contains only a reference to the actual data. It does NOT store the data attributes or geometry.

Here are some examples of adding layer files or packages to a map

// create a layer and add it to a groupLayer
string urlLyrx = @"\\Machine\SharedFolder\Census.lyrx";
Layer lyr = LayerFactory.Instance.CreateLayer(new Uri(urlLyrx), grpLayer)

// user the LayerCreationParams to customize the layer name
// KNOWN LIMIT  – no other properties of the LayerCreationParams (or 
//    FeaturelayerCreationParams etc) will be honored when used 
//    with lyrx or lpkx files.  
//    This is a known limit at ArcGIS Pro 2.4
string urlLpkx = @"\\Machine\SharedFolder\Census.lpkx";
var createParams = new LayerCreationParams(new Uri(urlLpkx))
{
  Name = "Population Density",
};

FeatureLayer flyr = LayerFactory.Instance.CreateLayer<FeatureLayer>(createParams,  myMap);

The LayerDocument class provides functionality to access and modify the contents of a layer file. Use this pattern to alter the layer file properties prior to adding it to the map. The following example alters the visibility and symbology of a layer file before adding it to a map.

string urlLyrx = @"\\Machine\SharedFolder\Census.lyrx";
var layerDoc = new ArcGIS.Desktop.Mapping.LayerDocument(urlLyrx);
// get the CIMLayerDocument
var cimLayerDoc = layerDoc.GetCIMLayerDocument();

// manipulate the layer definitions
//    change the visibility and renderer
var layerDefinitions = cimLayerDoc.LayerDefinitions;
var layerDef = layerDefinitions[0] as CIMFeatureLayer;
if (layerDef != null)
{
  layerDef.Visibility = false;
  layerDef.Renderer = new CIMSimpleRenderer()
  {
    Symbol = SymbolFactory.Instance.ConstructPolygonSymbol(
               CIMColor.CreateRGBColor(255, 0, 0)).MakeSymbolReference()
  };
}

// add it to the map
var createParams = new LayerCreationParams(cimLayerDoc);
LayerFactory.Instance.CreateLayer<Layer>(createParams, MapView.Active.Map);

You can also use the LayerDocument class to apply the symbology in the layer file to another layer in the map without requiring the layer file to be added to the map.

// default symbology 
CIMRenderer renderer = new CIMSimpleRenderer()
{
  Symbol = SymbolFactory.Instance.ConstructPolygonSymbol(
             CIMColor.CreateRGBColor(255, 0, 0)).MakeSymbolReference()
};

// load the layer file into a LayerDocument
var layerDoc = new ArcGIS.Desktop.Mapping.LayerDocument(@"D:\Pro_SDK\LayerDocument\LANDUSE_polygon_Edit.lyrx");
var cimLayerDoc = layerDoc.GetCIMLayerDocument();

// retrieve the renderer from the LayerDocument
var layerDefinitions = cimLayerDoc.LayerDefinitions;
var layerDef = layerDefinitions[0] as CIMFeatureLayer;
if (layerDef != null)
{
  renderer = layerDef.Renderer;
}

// apply the renderer to a layer in the map
// NOTE - the assumption is that the Landuse layer has the same fields
//   as the layer file in order for the renderer to display correctly
var fLayer = MapView.Active.Map.GetLayersAsFlattenedList().
            FirstOrDefault(l => l.Name == "Landuse") as FeatureLayer;
if (fLayer != null)
  fLayer.SetRenderer(renderer);

Querying features or rows using IDisplayTable

FeatureLayers and StandaloneTables are often entry points to accessing and selecting features and data. Both of these classes implement the IDisplayTable interface to support accessing, querying, and selecting features or rows. IDisplayTable also allows you to get the field list, set or get definition query, and so on. Note that (1) all IDisplayTable members are available directly from the FeatureLayer and StandaloneTable classes, you do not need to cast them to the IDiplayTable interface, and (2) these methods work properly when dealing with joined data.

To access the base table including joined tables, you need to use the GetTable method.

There is also a Search method for performing a search on a layer or a stand-alone table. This method returns a cursor of features or rows from the layer or the stand-alone table that meet a given search criteria. If there is a definition query set, Search will work on the subset that meets the definition criteria. Note that the Search method will not work on joined fields and returned values from joined fields; you do not need to anything special.

You can use the Select method to highlight features or rows on the map view or in the attribute table window.

//searching and returning number of features where value in 'Class' field in 'city'
QueryFilter qf = new QueryFilter()
  {
	WhereClause = "Class = 'city'"
  };

RowCursor rows = aFeatureLayer.Search(qf);

int i = 0;
while (rows.MoveNext()) i++;

Display Filters

Display filters are scale-dependent queries that specify which features of a layer are drawn at which scale ranges on a map. Display filters are ideal for use with dense feature datasets that have a large number of features being drawn at small scales. This can make it difficult to visually interpret the data and makes the layer slow to draw. Display filters differ from definition queries in the following two main ways:

  • Display filters affect the display of features only. Non-visible features that are excluded from the display by a display filter are still accessible by the layer for queries and selection. Filtered records will also still show up in the attribute table view. Definition queries, on the other hand, filter features entirely from the layer.
  • Display filters are scale dependent and are mutually exclusive. Scale ranges for display filters cannot overlap. Definition queries, meanwhile, can include or exclude the same sets of overlapping features depending on the nature of the underlying definition query filter.

Note: Some general information about Display Filters can be found here: ArcGIS Pro Use Display Filters

Modifying Display Filters

You can create and apply Display Filters to a feature layer using CIMDisplayFilter, part of the feature layer CIM (Cartographic Information Model) definition. Create a CIMDisplayFilter for each scale range that will be filtered. Set theMinScale and MaxScale properties of the CIMDisplayFilter to specify the scale range of the filter and provide a WhereClause to do the actual filtering (of features). Display filter scale ranges should not overlap.

In the following code snippet, a display filter is configured with an array of CIMDisplayFilters specifying different flow rates to show at different, consecutive, scale ranges for a hydrology dataset. The display filters are controlling the feature density at different scales without impacting the symbology or the underlying features:

     //Create a list of Display Filters
     var arrayDisplayFilters = new List<CIMDisplayFilter>()
     {
          new CIMDisplayFilter{ Name = "StreamOrder > 6", 
                  WhereClause = "StreamOrder > 6", MinScale= 0, MaxScale=50000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 5", 
                  WhereClause = "StreamOrder > 5", MinScale= 50000000, MaxScale=20000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 4", 
                  WhereClause = "StreamOrder > 4", MinScale= 20000000, MaxScale=5000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 3", 
                  WhereClause = "StreamOrder > 3", MinScale= 5000000, MaxScale=1000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 2", 
                  WhereClause = "StreamOrder > 2", MinScale= 1000000, MaxScale=100000},
          new CIMDisplayFilter{ Name = "StreamOrder > 1", 
                  WhereClause = "StreamOrder > 1", MinScale= 100000, MaxScale=24000},
     };

The display filters are applied via the layer CIM Definition CIMFeatureLayer.DisplayFilters* collection property. To use the display filter, the layer's CIMFeatureLayer.EnableDisplayFilters property, also on the CIM definition, must be set to true. Changes to the CIM are applied with a featureLayer.SetDefinition(...); call. A complete example is shown below:

  //Get the Hydrology layer from the TOC
  var hydrologyLyr = MapView.Active.Map.FindLayers("Hydrology").First() as FeatureLayer;
  await QueuedTask.Run(() => {

     //Get the CIM Definition
     var cimDefn = hydrologyLyr.GetDefinition() as CIMFeatureLayer;

     //Create a list of Display Filters
     var arrayDisplayFilters = new List<CIMDisplayFilter>()
     {
          new CIMDisplayFilter{ Name = "StreamOrder > 6", 
                  WhereClause = "StreamOrder > 6", MinScale= 0, MaxScale=50000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 5", 
                  WhereClause = "StreamOrder > 5", MinScale= 50000000, MaxScale=20000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 4", 
                  WhereClause = "StreamOrder > 4", MinScale= 20000000, MaxScale=5000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 3", 
                  WhereClause = "StreamOrder > 3", MinScale= 5000000, MaxScale=1000000},
          new CIMDisplayFilter{ Name = "StreamOrder > 2", 
                  WhereClause = "StreamOrder > 2", MinScale= 1000000, MaxScale=100000},
          new CIMDisplayFilter{ Name = "StreamOrder > 1", 
                  WhereClause = "StreamOrder > 1", MinScale= 100000, MaxScale=24000},
     };

     //apply the display filter collection to the CIM definition
     cimDefn.DisplayFilters = arrayDisplayFilters.ToArray();
     //make sure display filters are enabled
     cimDefn.EnableDisplayFilters = true;

     //apply the change to the CIM - note "filtered" features still show up in the attribute
     //table view and are available for query and selection...
     hydrologyLyr.SetDefinition(cimDefn);

  });

* CIMFeatureLayer.DisplayFilters can be null when EnableDisplayFilters is false.

To remove display filters, delete the relevant CIMDisplayFilter(s) from the layer DisplayFilters collection. To turn display filters "off", set the EnableDisplayFilters property to false. Any changes to the CIM must always be applied via a SetDefinition(...) call.

The below screenshots show the same USA hydrology dataset, from the snippet above, being drawn at different scale ranges and symbolized by flow volume with no display filter. Notice that the feature density obscures any meaningful information on the map at small scales:

DetailedHydrology

With the display filters applied, more and more features are progressively displayed as the map scale changes (and is "zoomed in"). At the smallest scales, only the rivers with the largest flow rates are displayed ("StreamOrder > 6"):

MajorRivers

At larger scales, the query of the relevant display filter (for "that" scale range) allows smaller flow-rate rivers to be draw, progressively adding to the map detail as the map is "zoomed in" ("StreamOrder > 4"):

SmallerRivers

Finally, at the largest scales, the display filters allow all features to draw, regardless of their flow rate attribution:

allRivers

A complete "Display Filter" sample is also available: DisplayFilters sample

Replacing data sources

There are numerous reasons why data sources need to be repaired or redirected to different locations. The idea of making these changes manually in every affected map document can be overwhelming. Methods are available with Map, Layer, and StandaloneTable classes to make these changes programmatically. You have control of updating data sources for individual map members, or you can update those that have a common workspace all at once.

FindAndReplaceWorkspacePath, available on Map, Layer, and StandaloneTable classes, allows you to substitute an entire or partial string for a layer or table's workspace path. It can't be used if the workspace type or dataset name has changed. It is ideal for scenarios where drive letters change, switch to UNC paths, update SDE connection file information, and so on.

ReplaceDatasource allows you to change the entire workspace path for all layers and tables that share the workspace path.

The Map class also provides the ChangeVersion method to switch and view data from a different geodatabase version.

Styles

StyleProjectItem represents a style in an ArcGIS Pro project. Styles are project items that contain reusable things such as symbols, colors, color schemes, and layout elements. The styles in a project can be retrieved as follows:

//Get all styles in the project
var styles = Project.Current.GetItems<StyleProjectItem>();

//Get a specific style in the project
StyleProjectItem style = styles.First(x => x.Name == "NameOfTheStyle");

ArcGIS Pro includes system styles that are installed and available to add to a project. These are read-only; you cannot modify their contents. In addition to the system styles, you can also create new custom styles and add these to your project as follows:

 //Full path for the new style file (.stylx) to be created
 string styleToCreate = @"C:\Temp\NewStyle.stylx";
 Project.Current.CreateStyle(styleToCreate);

When a new project is created, four system styles (ArcGIS 2D, ArcGIS 3D, ArcGIS Colors, and ColorBrewer Schemes RGB) are added to the project to ensure galleries of versatile symbols, colors, color schemes, and layout elements. You can also add other system styles or custom styles to a project. Galleries include items from all styles added to a project. Styles can be added to a project as follows:

//For ArcGIS Pro system styles, just pass in the name of the style to add to the project
Project.Current.AddStyle("3D Vehicles");

//For custom styles, pass in the full path to the style file on disk
string customStyleToAdd = @"C:\Temp\CustomStyle.stylx";
Project.Current.AddStyle(customStyleToAdd);

Removing a style from a project does not delete or otherwise change the style or its contents. Styles can be removed from a project as follows:

//For core ArcGIS Pro styles, just pass in the name of the style to remove from the project
roject.Current.RemoveStyle("3D Vehicles");

//For custom styles, pass in the full path to the style file on disk
string customStyleToRemove = @"C:\Temp\CustomStyle.stylx";
Project.Current.RemoveStyle(customStyleToRemove);

Upgrading styles

The underlying structure of symbols and styles may change with successive releases of ArcGIS Pro. That means that styles created in earlier versions may not be current. Styles that are not current can still be used in a project but will be read-only. You can check whether a style is current as follows:

//For custom styles, pass in the full path to the style file on disk
string customStyleToAdd = @"C:\Temp\CustomStyle.stylx";
Project.Current.AddStyle(customStyleToAdd);
StyleProjectItem style = Project.Current.GetItems<StyleProjectItem>().First(x => x.Path == customStyleToAdd);

//returns true if style matches the current Pro version
bool isCurrent = style.IsCurrent();  

If a style is not current, it can be upgraded. Upgrading will allow you to add, delete, and modify contents of the style.

//For custom styles, pass in the full path to the style file on disk
string customStyleToAdd = @"C:\Temp\CustomStyle.stylx";
Project.Current.AddStyle(customStyleToAdd);
StyleProjectItem style = Project.Current.GetItems<StyleProjectItem>().First(x => x.Path == customStyleToAdd);

//Upgrade style
if (style.CanBeUpgraded())
{
 StyleHelper.UpgradeStyle(style);
}

Style Item

StyleItem represents an item contained in a style. A style can contain the following types of style items:

  • SymbolStyleItem—Can be of the following types:

    • Point symbol—Symbol to draw point features.
    • Line symbol—Symbol to draw line features.
    • Polygon symbol—Symbol to draw polygon features.
    • Text symbol—Symbol to draw labels or annotation features.
  • ColorStyleItem—Single color defined by values in a supported color model.

  • ColorRampStyleItem—Array of colors in a specified pattern or scheme.

  • NorthArrowStyleItem—Directional layout element used to indicate north orientation.

  • ScaleBarStyleItem—Layout element that graphically indicates map scale.

  • LabelPlacementStyleItem—Can be of the following types:

    • Standard label placement—Set of rules based on the standard label engine determining how labels will be placed in relation to features.
    • Maplex label placement—Set of rules based on the Maplex label engine determining how labels will be placed in relation to features.

You can search for a specific type of style items in a style. For example, this is how to search for point symbols in a style:

//Get all point symbols in a style - pass in an empty string for the searchString parameter
IList<SymbolStyleItem> allPointSymbols = style.SearchSymbols(StyleItemType.PointSymbol, "");

//Get point symbols in a style that satisfy the search criteria
IList<SymbolStyleItem> pointSymbols = style.SearchSymbols(StyleItemType.PointSymbol, "red");

Similarly, you can also search for other types of style items. For example, here is how to search for colors in a style:

//Get colors in a style that satisfy the search criteria
IList<ColorStyleItem> colors = style.SearchColors("blue");

Each style item in a style has a unique key, and this key can be used to retrieve the specific style item from a style as follows:

//Get a specific style item from a style based on the KEY of the style item
//In this example, "Cone_Volume_3" is the KEY of the Cone symbol in the "ArcGIS 3D" system style
SymbolStyleItem redCone = await QueuedTask.Run<SymbolStyleItem>(() =>
    {
         return (SymbolStyleItem) style.LookupItem(StyleItemType.PointSymbol, "Cone_Volume_3");
    });

Style items can be added or removed from a style as follows:

//Add a style item to a style
await QueuedTask.Run(() =>
{
      style.AddItem(styleItemToAdd);
});

//Remove a style item from a style
await QueuedTask.Run(() =>
{
      style.RemoveItem(styleItemToRemove);
});

Symbol in a SymbolStyleItem can be retrieved as follows:

//Get symbol from SymbolStyleItem
CIMSymbol symbol = await QueuedTask.Run<CIMSymbol>(() =>
{
      return symbolStyleItem.Symbol;
});

Similarly, you can also retrieve color, color ramp, north arrow, and so on, from their respective style items. For example, this is how you can get a north arrow from NorthArrowStyleItem:

//Get north arrow from NorthArrowStyleItem
CIMNorthArrow northArrow = await QueuedTask.Run<CIMNorthArrow>(() =>
{
      return northArrowItem.NorthArrow;
});

The StyleItem class is designed to support data binding, which helps in creation of custom symbol galleries. Style items can be retrieved from a style based on search criteria, and the results can be displayed along with a preview image for each style item in a gallery. The CustomSymbolPicker SDK sample shows how this can be accomplished.

Creating and editing symbols

SymbolFactory.Instance provides many static methods and properties to create new symbols. For example, new symbols can be created as follows:

//Construct a point symbol of a specific color, size and shape
CIMPointSymbol pointSymbol = await QueuedTask.Run<CIMPointSymbol>(() =>
{
      return SymbolFactory.Instance.ConstructPointSymbol(ColorFactory.Instance.RedRGB, 10.0, SimpleMarkerStyle.Star);
});

//Construct a polygon symbol of specific color and fill style
CIMPolygonSymbol polygonSymbol = SymbolFactory.Instance.ConstructPolygonSymbol(ColorFactory.Instance.RedRGB, SimpleFillStyle.Solid);

//Construct a line symbol of specific color, size and line style
CIMLineSymbol lineSymbol = SymbolFactory.Instance.ConstructLineSymbol(ColorFactory.Instance.BlueRGB, 4.0, SimpleLineStyle.Solid);

Methods are also provided for editing some properties of symbols. For example:

//Set rotation of point symbol
pointSymbol.SetAngle(30.0);

//Set width of line symbol
lineSymbol.SetSize(4.0);

In 3D scenes, point, line and polygon symbols can be displayed either in real-world units or in page units. If the symbol is displayed in real-world units, the symbol will scale with distance, which means that the symbol will display larger when you zoom in closer and smaller as you zoom away from the symbol. When creating new symbols, it is important to set the real-world unit setting of the symbol to be the same as that of the feature layer so that you do not see unexpected display results because of a mismatch. Here is how this can be done:

//Set real world setting of symbol to be the same as that of the feature layer
pointSymbol.SetRealWorldUnits(ftrLayer.UsesRealWorldSymbolSizes);

Using symbols with renderers

To update the symbol of a feature layer, you first get the feature layer's current renderer by calling the GetRenderer method, set the renderer's symbol reference to the symbol reference of the new symbol, and update the feature layer renderer by calling the SetRenderer method. For example, here is how the symbol can be updated for a feature layer symbolized with a simple renderer:

//Get simple renderer from the feature layer
CIMSimpleRenderer currentRenderer = ftrLayer.GetRenderer() as CIMSimpleRenderer;

//Set the symbol's real world setting to be the same as that of the feature layer
symbolToApply.SetRealWorldUnits(ftrLayer.UsesRealWorldSymbolSizes);

//Update the symbol reference of the current simple renderer
currentRenderer.Symbol = symbolToApply.MakeSymbolReference();

//Update the feature layer renderer
ftrLayer.SetRenderer(currentRenderer);

Device Location API, GNSS/GPS Data

You can connect a global navigation device, such as a GNSS receiver, to ArcGIS Pro, via the Device Location API, to view your current location in a map or scene. GNSS, or Global Navigation Satellite System, is the standard generic term used for satellite navigation systems that provide autonomous geo-spatial positioning with global coverage. The Device Location API allows you to connect to, and visualize and access data from, a connected GNSS device.

Connect to a GNSS Device

To connect a GNSS device to Pro, it must support output in the form of National Marine Electronics Association, NMEA, sentences. Add-ins will use the ArcGIS.Desktop.Core.DeviceLocation.DeviceLocationService.Instance singleton to open, close, and update connections to GNSS devices. Multiple GNSS devices can be connected to a machine, however, only one device can be set as the currently active, or "open", connection. To open a connection, add-ins must specify a DeviceLocationSource and, optionally, any associated DeviceLocationProperties. Currently, only one device location source is supported: a SerialPortDeviceLocationSource source object. As a minimum, the ComPort the device location source is connected to must be specified. If a spatial reference is not specified then WGS84 is assumed. If a baud rate is not specified, 4800 is assumed. The DeviceLocationProperties parameter can be used to set the AccuracyThreshold if necessary.

  var newSrc = new SerialPortDeviceLocationSource();
  newSrc.ComPort = "Com3";//required
  newSrc.BaudRate = 4800; //optional
  newSrc.AntennaHeight = 3;  // meters
                             //Sr WGS84 will be assumed

  var props = new DeviceLocationProperties();
  props.AccuracyThreshold = 10;   // meters

  try
  {
    // jump to the background thread
    await QueuedTask.Run(() =>
    {
      // open the source
      DeviceLocationService.Instance.Open(newSrc, props);
    });
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message);
  }

Add-ins can call DeviceLocationService.Instance.IsDeviceConnected() to determine if a source is currently connected and DeviceLocationService.Instance.GetSource() to retrieve its connection parameters.

  if (DeviceLocationService.Instance.IsDeviceConnected()) {
    var src = DeviceLocationService.Instance.GetSource();
    //Get the connection source properties
    if (src is SerialPortDeviceLocationSource serialPortSrc) {
          var port = serialPortSrc.ComPort;
          ... //etc

Add-ins can disconnect from the current GNSS device by calling the DeviceLocationService.Instance.Close() method.

  await QueuedTask.Run(() =>
  {
    //If no device is connected, close is a no-op
    DeviceLocationService.Instance.Close();
  });

Accessing GNSS data

The current snapshot data can be retrieved via the DeviceLocationSource.Instance.GetCurrentSnapshot() method. Snapshots are of type NMEASnapshot; a data structure used to represent a feed from a device that conforms to the NMEA spectifications.

In the following snippet, the latest snapshot is being retrieved and added to a graphics layer:

  // ensure there is a device
  var source = DeviceLocationService.Instance.GetSource();
  if (source == null)
     return

  bool validPoint = await QueuedTask.Run(() =>
  {
    // get the last location
    var snapshot = DeviceLocationService.Instance.GetCurrentSnapshot();

    var pt = snapshot?.GetPositionAsMapPoint();
    if ((pt == null) || (pt.IsEmpty))
      return false;

    // create symbol
    var ptSymbol = SymbolFactory.Instance.ConstructPointSymbol(
                       CIMColor.CreateRGBColor(125, 125, 0), 10, SimpleMarkerStyle.Triangle);

    // Add it to the graphics layer
    graphicsLayer.AddElement(pt, ptSymbol);
    graphicsLayer.ClearSelection();
    return true;
  });

In addition to the current snapshot, add-ins can subscribe to the SnapshotChangedEvent (in ArcGIS.Desktop.Core.DeviceLocation.Events namespace). This event will be raised each time a new snapshot is received (from the open device).

  SnapshotChangedEvent.Subscribe(OnSnapshotChanged)

  private void OnSnapshotChanged(SnapshotChangedEventArgs args)
  {
    if (args == null)
      return;

    var snapshot = args.Snapshot as NMEASnapshot;
    if (snapshot == null)
      return;

    QueuedTask.Run(() =>
    {
      var pt = snapshot.GetPositionAsMapPoint();        

      // examine properties of MMEASnapshot
      // snapShot.DateTime, snapshot.Altitiude
      // snapshot.Latitude, snapshot.Longitude
      // snapshot.VDOP  etc

      //TODO - use returned point
    });
  };

Note: Use the editing api EditOperation.Create to create features with the current device location coordinates.

Interacting with the map

To start tracking the location updates, or "snapshots", on the active view, the open device location source must be enabled (on the current map). Add-ins enable (and disable) the open device location source via MapDeviceLocationService.Instance singleton object calling MapDeviceLocationService.Instance.SetDeviceLocationEnabled(true | false). Before enabling the device location source, add-ins should check that there is an active view and map and there is an open device location source or an InvalidOperationException will be thrown.

 //Enable the current device location source...

 //There must be an active map or an InvalidOperationException will be thrown
  if (MapView.Active?.Map == null)
     return;
  //There must be an open device location source or an InvalidOperationException will be thrown
  if (!DeviceLocationService.Instance.IsDeviceConnected())
     return;
  //Enable the device
  QueuedTask.Run(() => MapDeviceLocationService.Instance.SetDeviceLocationEnabled(true));

Once the device location source has been enabled on the current map, various options associated with how the snapshots can be tracked can be set on the map via MapDeviceLocationService.Instance.SetDeviceLocationOptions(options). The map will automatically track the incoming location snapshots (once enabled) depending on how the MapDeviceLocationOptions have been configured. The map can also be explicitly panned or zoomed to the most recent snapshot via MapDeviceLocationService.Instance.ZoomOrPanToCurrentLocation(true | false) to zoom or pan respectively. Calling either SetDeviceLocationOptions or ZoomOrPanToCurrentLocation with no device location source enabled will throw an InvalidOperationException.

Configuring the display of the current feed and then zooming to the current snapshot location is shown in the following example:

  //There must be an active map or an InvalidOperationException will be thrown
  if (MapView.Active?.Map == null)
     return;
  //There must be an open device location source or an InvalidOperationException will be thrown
  if (!DeviceLocationService.Instance.IsDeviceConnected())
     return;

  //Get the current options
  var options = MapDeviceLocationService.Instance.GetDeviceLocationOptions();

  //Configure...
  options.DeviceLocationVisibility = true; // show the device location
  options.NavigationMode = MappingDeviceLocationNavigationMode.KeepAtCenter;
  options.TrackUpNavigation = LocationOptions_TrackUp;

  // update the options
QueuedTask.Run(() => {

  MapDeviceLocationService.Instance.SetDeviceLocationOptions(options);
  //zoom to the most recent snapshot location
  MapDeviceLocationService.Instance.ZoomOrPanToCurrentLocation(true);

});

UI controls

Coordinate System Picker

The Coordinate System Picker is a UI component similar to the core Coordinate Picker on the ArcGIS Pro Map property sheet. Use this within an Add-in to allow users to pick a spatial reference. Configure the control using a CoordinateSystemControlProperties object

xmlns:mapping="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<Border BorderBrush="{DynamicResource Esri_BorderBrush}" BorderThickness="2" Margin="2">
  <mapping:CoordinateSystemsControl  x:Name="CoordinateSystemsControl"
                          ConfigureControl="{Binding Path=ControlProperties}"/>
</Border>

coordinatesystempicker

A sample using the Coordinate System Picker can be found at Coordinate Systems Dialog.

Coordinate System Details

The Coordinate System Details control displays the properties of a specified spatial reference. It can be used in conjunction with a Coordinate System Picker which provides a UI for choosing a spatial reference or used alone.

xmlns:mapping="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<mapping:CoordinateSystemsDetailsControl 
         SpatialReference="{Binding ElementName=CoordinateSystemsControl, Path=SelectedSpatialReference}"
         ShowVerticalCSDetails="true"/>

coordinatesystemdetails

A sample using the Coordinate System Picker and the Coordinate System Detail control can be found at Coordinate Systems Dialog.

Symbol Searcher Control

The Symbol Searcher Control is used to search for StyleItems (Point/Line/Polygon symbols, colors, text symbols, etc.). The Symbol Searcher Control can be used in conjunction with the Symbol Picker Control or as a stand alone control.

To add a 'Symbol Searcher Control' to an add-in DockPane you have to add the following snippets to your View (XAML). Note that the properties: SearchOutputOptions, SearchFilterStyle, and SearchFilterType are all optional.

Symbol Searcher/Picker Control

xmlns:controls="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<StackPanel Grid.Row="1" Orientation="Vertical">
  <controls:SymbolSearcherControl x:Name="SymbolSearch" 
    SearchOutputOptions="{Binding SearchOutputOptions}"
    SearchFilterStyle="{Binding SearchFilterStyle}"
    SearchFilterType="{Binding SearchFilterType}"
    SearchPauseAutoSearch="{Binding SearchPauseSearching}" />
  <controls:SymbolPickerControl x:Name="SymbolPicker" 
    PickerStyleItems="{Binding Path=SearchResultStyleItems, ElementName=SymbolSearch, Mode=OneWay}"
    SelectedPickerStyleItem="{Binding SelectedPickerStyleItem, Mode=TwoWay}"
    ViewingOption="{Binding PickerViewOption, Mode=TwoWay}"
    GroupingOption="{Binding PickerGroupOption, Mode=TwoWay}"
    ShowOptionsDropDown="{Binding Path=IsChecked, ElementName=ShowOptionsCheckBox, Mode=OneWay}"
    HorizontalAlignment="Stretch"/>
</StackPanel>

and the corresponding properties that are added to your ViewModel code behind class:

using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Controls;

protected CTOR_for_ViewModel()
{
  SearchPauseSearching = false;
}

private bool _searchPauseSearching = true; 
public bool SearchPauseSearching
{
  private get
  {
    return _searchPauseSearching;
  }
  set
  {
    SetProperty(ref _searchPauseSearching, value, () => SearchPauseSearching);
    System.Diagnostics.Debug.WriteLine($@"==== SearchPauseSearching setter: {_searchPauseSearching}");
  }
}

private StyleItem _selectedPickerStyleItem;
public StyleItem SelectedPickerStyleItem
{
  get
  {
    return _selectedPickerStyleItem;
  }
  set
  {
    SetProperty(ref _selectedPickerStyleItem, value, () => SelectedPickerStyleItem);
    MessageBox.Show($@"SelectedPickerStyleItem: {_selectedPickerStyleItem?.Name}");
  }
}

SymbolSearcherPickerControl

Symbol Searcher Standalone Control

xmlns:controls="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<StackPanel Orientation="Vertical">
  <controls:SymbolSearcherControl x:Name="SymbolSearch"
				    HorizontalAlignment="Stretch"
            VerticalAlignment="Top"/>
  <ListBox x:Name="SymbolPicker"
		  ItemsSource="{Binding Path=SearchResultStyleItems, ElementName=SymbolSearch, Mode=OneWay}"
      SelectedItem="{Binding SelectedPickerStyleItem}"
      DisplayMemberPath="Name"
		  HorizontalAlignment="Stretch"
      VerticalAlignment="Top" MaxHeight="450"/>
</StackPanel>

and the corresponding properties that are added to your ViewModel code behind class:

using ArcGIS.Desktop.Mapping;
using ArcGIS.Desktop.Mapping.Controls;

protected CTOR_for_ViewModel()
{
  SearchPauseSearching = false;
}

private StyleItem _selectedPickerStyleItem;
public StyleItem SelectedPickerStyleItem
{
  get
  {
    return _selectedPickerStyleItem;
  }
  set
  {
    SetProperty(ref _selectedPickerStyleItem, value, () => SelectedPickerStyleItem);
    MessageBox.Show($@"SelectedPickerStyleItem: {_selectedPickerStyleItem?.Name}");
  }
}

SymbolSearcherStandaloneControl

Samples using the Symbol Searcher and Symbol Picker control can be found at Using Symbol Searcher Control.

Symbol Picker Control

The Symbol Picker Control is used to 'pick' a specific StyleItem from a given list of StyleItems. The Symbol Picker Control can be used in conjunction with the Symbol Searcher Control to prime the list of StyleItems to pick from and it can be used alone.

SymbolPickerControl

See the SymbolPickerControl used in conjunction with the SymbolSearcherControl here: Symbol Searcher/Picker Control.

Symbol Picker Standalone Control

xmlns:controls="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<controls:SymbolPickerControl x:Name="SymbolPicker"
		PickerStyleItems="{Binding Path=PickerStyleItems, Mode=OneWay}"
    SelectedPickerStyleItem="{Binding SelectedPickerStyleItem}"
		ViewingOption="{Binding ViewingOption}"
		GroupingOption="{Binding GroupingOption}"
		ShowOptionsDropDown="{Binding ShowOptionsDropDown, Mode=OneWay}"
    />

and the corresponding properties that are added to your ViewModel code behind class:

protected override Task InitializeAsync()
{
	return QueuedTask.Run(() =>
	{
		//Search for symbols in the selected style
		StyleProjectItem si = Project.Current.GetItems<StyleProjectItem>().FirstOrDefault();
		if (si != null)
		{
			var lstStyleItems = si.SearchSymbols(StyleItemType.PointSymbol, string.Empty).Select((s) => s as StyleItem);
			RunOnUIThread(() =>
			{
				_pickerStyleItems = new ObservableCollection<StyleItem>();
				_pickerStyleItems.AddRange(lstStyleItems);
				NotifyPropertyChanged(() => PickerStyleItems);
			});
		}
	});
}

private ObservableCollection<StyleItem> _pickerStyleItems;
public ObservableCollection<StyleItem> PickerStyleItems
{
	get { return _pickerStyleItems; }
	set
	{
		SetProperty(ref _pickerStyleItems, value, () => PickerStyleItems);
	}
}

private StyleItem _selectedPickerStyleItem;
public StyleItem SelectedPickerStyleItem
{
	get { return _selectedPickerStyleItem; }
	set
	{
		SetProperty(ref _selectedPickerStyleItem, value, () => SelectedPickerStyleItem);
		MessageBox.Show($@"SelectedPickerStyleItem: {_selectedPickerStyleItem?.Name}");
	}
}

private PickerViewOption _viewingOption;
public PickerViewOption ViewingOption
{
	get { return _viewingOption; }
	set
	{
		SetProperty(ref _viewingOption, value, () => ViewingOption);
	}
}

private PickerGroupOption _groupingOption;
public PickerGroupOption GroupingOption
{
	get { return _groupingOption; }
	set
	{
		SetProperty(ref _groupingOption, value, () => GroupingOption);
	}
}

private bool _showOptionsDropDown;
public bool ShowOptionsDropDown
{
	get { return _showOptionsDropDown; }
	set
	{
		SetProperty(ref _showOptionsDropDown, value, () => ShowOptionsDropDown);
	}
}

#region Helpers

/// <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 Task RunOnUIThread(Action action)
{
	if (OnUIThread)
	{
		action();
		return Task.FromResult(0);
	}
	else
		return Task.Factory.StartNew(action, System.Threading.CancellationToken.None, TaskCreationOptions.None, QueuedTask.UIScheduler);
}

/// <summary>
/// Determines if the application is currently on the UI thread
/// </summary>
private static bool OnUIThread
{
	get
	{
		if (FrameworkApplication.TestMode)
			return QueuedTask.OnWorker;
		else
			return System.Windows.Application.Current.Dispatcher.CheckAccess();
	}
}

#endregion Helpers

SymbolPickerStandaloneControl

Samples using the Symbol Searcher and Symbol Picker control can be found at Using Symbol Searcher Control.

Query Builder

The Query Builder control provides a UI for building a query expression against a layer or table. Configure the query builder with the layer or table and optional expression using a QueryBuilderControlProperties object.

xmlns:mapping="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<mapping:QueryBuilderControl x:Name="QueryBuilderControl"  
                               ConfigureControl="{Binding Path=ControlProperties}"/>

querybuilder

A sample using the Query Builder control can be found at QueryBuilderControl.

Geocode control

The Geocode Locator control provides a UI for geocoding. It looks and acts the same as the Locate dockpane. It allows you to add, remove, reorder, enable and disable locators. It also provides functionality to search and display geocode results.

xmlns:mapping="clr-namespace:ArcGIS.Desktop.Mapping.Controls;assembly=ArcGIS.Desktop.Mapping"

<mappingControls:LocatorControl x:Name="locator" />

locatorControl

A sample using the Geocode Locator control can be found at GeocodingTools.

⚠️ **GitHub.com Fallback** ⚠️