ProConcepts Map Exploration - Esri/arcgis-pro-sdk GitHub Wiki

The mapping functionality in ArcGIS Pro is delivered through the ArcGIS.Desktop.Mapping assembly. Map Exploration provides classes and members that support navigating and interacting with views of a map. This includes modifying the view's extent by zooming to layers, features, and bookmarks, and interactively selecting or returning features that intersect a given sketch geometry.

  • ArcGIS.Desktop.Mapping.dll
  • ArcGIS.Desktop.Extensions.dll
Language:      C#
Subject:       Map Exploration
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          10/06/2024
ArcGIS Pro:    3.4
Visual Studio: 2022

In this topic

MapView

A project can contain multiple maps, either 2D or 3D, and each defines the collection of layers that make up that map. A map view is simply a view of a map. Map views are the primary interface used to display, navigate, select, identify, and edit data in a 2D or 3D map. The MapView class provides properties and methods to navigate and interact with layers in the map. The map being visualized in the view can be accessed via the Map property.

There can be multiple map views open at a given time, but there can only be one active map view. The active map view will set the context for the ribbon and many of the dock panes in the application. For example, the map Contents pane will reflect the layers of the active map view's map. The instance of the active map view can be accessed via the static Active property on MapView. The active property will return null if there is no active map view. This is useful when writing commands designed to interact with the active map.

Active Map View

Drawing mode of 3D map views can be set via the SetSceneDrawingMode method. The scene drawing mode can be either perspective or isometric. Default drawing mode is perspective and new 3D map views always open in perspective mode. Scene's field of view angle can be changed using the SetFieldOfView method.

The map view also provides the context for the selected items in the Contents pane (a.k.a. "TOC"). For example, the GetSelectedLayers method returns the collection of layers that are currently selected in the Contents pane. This context is used to determine which contextual tabs to show, for example, when one or more feature layers are selected, the Feature Layer tab group will display, and the commands will act on the selected feature layers. This is useful when writing commands to work with the selected items in the Contents pane.

Navigation

The map view's camera defines the area being displayed in the map. The camera can be accessed via the Camera property on the map view. You can set the map view's camera by using the ZoomTo(Camera) method. In addition to directly setting the camera, there are several overload methods for ZoomTo and PanTo that can be used to zoom to the extent of a layer, the definition of a bookmark, a specific geometry, and so on.

Select and identify

In some cases, you may want to provide a tool that allows the user to interactively click and sketch in the map and select or get a collection of features that intersect the sketch. The MapTool abstract class provides an easy way to author a tool that can create a sketch in the map view and return the geometry from the sketch. This geometry can then be passed into the SelectFeatures or GetFeatures method to select or return the collection of features that intersect the geometry.

Link Chart views

Starting at ArcGIS Pro 3.3, a new type of map, the "link chart" or "link chart map" is added to the public api. Link charts are displayed on link chart views. Link chart views use the MapView class same as 2d and 3d map and scene views. Link chart maps are accessed off the MapView.Active.Map application context same as any other map type if a link chart is the current active view. To determine if a given MapView is for a link chart or not, use the MapView.IsLinkChartView property.

  //Check the active view...
  if (MapView.Active.IsLinkChartView) {
    //the current active view is a link chart...

  }

  //Find a particular link chart view amongst the open map panes
  var mapPanes = FrameworkApplication.Panes.OfType<IMapPane>().ToList();
  var mapPane = mapPanes.FirstOrDefault(
      mp => mp.MapView.IsLinkChartView && mp.MapView.Map.Name == "Acme Link Chart");
  var linkChartMap = mapPane.MapView.Map;

  //etc.

Refer to the ProConcepts Knowledge Graph for more information about Knowledge Graphs and Link Charts.

Camera

The camera serves as a tool to display the world or map, and it can be moved through space to change your view. The camera describes your active point of view, in both 2D and 3D, on the map. You can get the current camera from the view using the Camera property. You can set the camera by calling the ZoomTo(Camera) method and passing in the desired camera.

The virtual camera is your window to the map. Much like a physical camera you can manipulate in the real world, the ArcGIS Pro virtual camera is moved around to view the map (in both 2D and 3D). The movement of the camera is done in coordinates of the map or the 3D axis system (X, Y, and Z).

Camera in a 3D Scene

Viewing modes

Both 2D and 3D properties are available on the camera object. If the map view's ViewingMode is Map, you can set 2D properties. If the ViewingMode is SceneGlobal or SceneLocal, you can set 3D properties.

2D camera

In 2D, the camera defines its viewing location using X and Y. The values are in the same units as those defined by the camera’s spatial reference. This will be the center point of the 2D view’s extent. The viewing direction, or rotation, of the map is defined using the camera’s Heading property. The camera’s Scale property then defines how far the view is zoomed in or out. The value is expressed as a double representing the value on the right side of the ratio. For example, a value of 1000 is equal to a scale of 1:1000.

3D camera

In 3D, the camera defines its viewing location using X, Y, and Z. As with 2D, the values are stored in the same units as those defined by the camera’s spatial reference, with the additional possibility that XY units and Z units may be different. The viewing direction is defined by a combination of the Heading, Pitch, and Roll properties, which rotate, tilt, and roll how the camera looks at the content in the map.

Camera movement in a 3D Scene

Viewpoint

You can use a camera to define either the coordinate you're looking from (the observer) or the position that you are looking at (the target) using the Viewpoint property. If you set the Viewpoint to LookFrom, the camera viewing location will be the coordinate of the observer. If you set the Viewpoint to LookAt, the camera viewing location will be the coordinate of the target. The camera returned from the map view's Camera property will always have a Viewpoint of LookAt.

TableView

A table view is a view of a table or feature class. Table views are the primary interface for displaying, selecting and editing data in a tabular fashion. The TableView class provides properties and methods to navigate and interact with the table.

There can be multiple table views open at a given time, but there can only be one active table view. The active table view will set the context for the ribbon and the Table of Contents. The instance of the active table view can be accessed via the static Active property on TableView. The active property will return null if there is no active table view.

Table View

Typically a table view is linked to a layer or standalone table that is in a map. When visualized in this way use the MapMember property to obtain the underlying object. However, a table view can also be linked to a table or featureclass directly from an external geodatabase (ie, the table or featureclass are not in the map). In this case, the underlying object can be accessed with the Item property. Use ArcGIS.Desktop.Core.ItemFactory.CanGetDataset and ArcGIS.Desktop.Core.ItemFactory.GetDataset to retrieve the data.

Here's an example of a table view displaying data from an external source. See how the context of the Table of Contents and ribbon are different from the above picture showing table views containing map data.

External Table Views

The following snippet shows how to access the active table view.

   // get the active table view
   var activeTV = TableView.Active;   

   // get the active map member 
   var mm = activeTV.MapMember;

   // it could be a feature layer
   var fl = mm as FeatureLayer;
   // or it could be a standalone table
   var st = mm as StandaloneTable;

   // if mapMember is null, then the data has come from an external source
   // use TableView.Item
   var item = activeTV.Item;

You can set the view mode (All records or Selected records) and zoom level of the table view with the SetViewMode and SetZoomLevel methods. There are also properties to get the current ViewMode and ZoomLevel values.

   // get the active table view
   var tableView = TableView.Active;  
   // set the zoom level
   tableView.SetZoomLevel(200);

   // toggle view mode
   var vmode = tableView.ViewMode;
   tableView.SetViewMode(
      vmode == TableViewMode.eAllRecords ? TableViewMode.eSelectedRecords : TableViewMode.eAllRecords);

If you wish to show tabular data hosted on a dockpane or form, use the TableControl. All the functionalities discussed in the sections below, apply to both the TableView and TableControl.

Row Indexes and Object IDs

Access to rows in the table view is typically achieved using the row index. You can find the active row index using the ActiveRowIndex property. Navigate between rows by modifying the ActiveRowIndex property or by using the BringIntoView function which also scrolls the table view to that row. You can retrieve the object ID of a particular row by using the GetObjectIdAsync method

In many cases however, you may only know the ObjectID of the row that you wish to make current. In this case, use the GetRowIndex or GetRowIndexAsync methods to determine the row index for a given ObjectID in the dataset and then use the ActiveRowIndex or BringIntoView function to set the row.

   var tableView = TableView.Active;   
   if (tableView != null)
   {
     // get the active rowindex
     int rowIndex = tableView.ActiveRowIndex;
     // get the active OID
     long? OID = tableView.ActiveObjectId;
     long newOID = -1;

     // move to a different row
     var newIndex = 10 + rowIndex;
     await tableView.BringIntoView(newIndex);
     newOID = await tableView.GetObjectIdAsync(newIndex);


     // or move to a different row via objectID
     newOID = OID.Value + 20;
     var idx = await tableView.GetRowIndexAsync(newOID, true);
     if (idx != -1)
       await tableView.BringIntoView(idx);
  }

Selected and Highlighted Rows

Selected rows are displayed in the table view with a cyan highlight. When the table view is in Selected records view mode, you can work with a subset of the selected records by highlighting records. Highlighted records appear yellow in both the table and the map or scene view. The benefit of being able to highlight records within a selection set is that you are identifying specific features from an already defined subgroup. Note that selection is persisted but highlighting is not. That is, if you close and reopen the map, or change the selection, the highlighting is cleared, but the selection remains.

There are numerous methods to manipulate the row selection using the TableView class. You can select records by either object ID or row index using the Select method, or you can toggle the current row selection with the ToggleRowSelection method. Other options include SwitchSelection, SelectAll or ClearSelection. Each of these methods should be preceded in use by its corresponding CanXXX property (for example CanSelect, CanToggleRowSelection, CanSwitchSelection, CanClearSelection.

You can retrieve the set of selected records using either of the GetSelectedObjectIds or GetSelectedRowIndexes methods.

See the following code snippet as an example.

   var tv = TableView.Active;
   if (tv == null)
     return;

   QueuedTask.Run(async () =>
   {
      // get the set of selected objectIDs 
      var selOids = tv.GetSelectedObjectIds();
      // get the set of selected row indexes
      var selRows = tv.GetSelectedRowIndexes();

      // add to the set of selected records
      var newoids = new List<long>(selOids);
      newoids.AddRange(new List<long>() { 10, 15, 17 });
      tv.Select(newoids, true);

      // or switch the current selection
      if (tv.CanSwitchSelection)
        tv.SwitchSelection();

      // toggle the active rows selection
      if (tv.CanToggleRowSelection)
        tv.ToggleRowSelection();

      // clear the selection
      if (tv.CanClearSelection)
        tv.ClearSelection();
   });

Similar methods exist to manipulate and retrieve the highlighted records when the table view is in Selected records view mode. Retrieve the highlighted records using GetHighightedObjectIds. Use CanHighlight, Highlight, CanToggleRowHighlight, ToggleRowHighlight, CanSwitchHighlight, SwitchHighlight or CanClearHighlighted and ClearHighlighted to modify the highlighted set.

   var tv = TableView.Active;
   if (tv == null)
     return;

   QueuedTask.Run(() =>
   {
      // highlighted records are only accessible when viewMode = selected records
      if (tv.ViewMode == TableViewMode.eSelectedRecords)
      {
        // get list of current highlighted objectIDs
        var highlightedObjectIDs = tv.GetHighlightedObjectIds();

        // get list of current selected objectIDs
        var selectedObjectIds = tv.GetSelectedObjectIds();
        var idsToHighlight = new List<long>();

        // add the first two selected objectIds to highlight
        if (selectedObjectIds.Count >= 2)
        {
          idsToHighlight.Add(selectedObjectIds[0]);
          idsToHighlight.Add(selectedObjectIds[1]);
        }

        // highlight
        if (tv.CanHighlight)
          tv.Highlight(idsToHighlight, true);

        // or clear the highlight
        //if (tv.CanClearHighlighted)
        //  tv.ClearHighlighted();

        // or switch the highlight of the active row
        //if (tv.CanSwitchHighlight)
        //  tv.SwitchHighlight();
      }
    });

Once you have a set of selected or highlighted records you can zoom or pan to those records using the ZoomToSelected, PanToSelected, ZoomToHiglighted or PanToHighlighted methods. Precede each of these methods with the appropriate CanXXX property to ensure that the action is valid.

   var tv = TableView.Active;
   if (tv == null)
     return;
     
   if (tv.CanZoomToSelected)
     tv.ZoomToSelected();

Table Fields

Specific fields in the table view can be accessed by GetField or a combination of GetFieldIndex to obtain a field index followed by a GetField call (with that index). The collection of fields in the table view can be retrieved with the GetFields method.

Set the active field with the SetActiveField method using either the field index or the field name. Retrieve the active field index using ActiveFieldIndex.

The TableView class also has a number of methods and properties to allow you to alter the display by manipulating the table fields. Options include selecting fields, customize showing the field alias or field name, setting the field order, hiding fields, freezing fields (freezing fields means that the field column is aligned to the left of the table and kept visible as you scroll horizontally) or sorting the table.

See the following snippets for examples of the above actions.

   var tv = TableView.Active;
   if (tv == null)
      return;

   // field access
   var flds = tv.GetFields();
   var fldIdx = tv.GetFieldIndex("STATE_NAME");
   var fldDesc = tv.GetField(fldIdx);

   var activeIdx = tv.ActiveFieldIndex;
   tv.SetActiveField("STATE_NAME");

 
   // select a set of fields
   var selectedfields = tv.GetSelectedFields();
   tv.SetSelectedFields(new List<string> { "CITY_FIPS", "STATE_FIPS" });
   selectedfields = tv.GetSelectedFields();


   // hide fields
   tv.ShowAllFields();
   tv.SetHiddenFields(new List<string> { "CITY_FIPS", "STATE_FIPS" });
   // adds to hidden
   tv.SetHiddenFields(new List<string> { "STATE_NAME" });


   // sort fields
   var dict = new Dictionary<string, FieldSortInfo>();
   dict.Add("STATE_NAME", FieldSortInfo.Asc);
   dict.Add("CITY_NAME", FieldSortInfo.Desc);
   await tv.SortAsync(dict);


   var fields = tv.GetFields();
   tv.ResetFieldOrder();

   var fldOrder = new List<string>();
   fldOrder.Add("STATE_NAME");
   fldOrder.Add("STATE_FIPS");
   await tv.SetFieldOrderAsync(fldOrder);

ITablePane, ITablePaneEx, IExternalTablePane

As mentioned before, the table view can contain data that is either within the map or from an external source. Each view has a pane associated with it - for more information on panes see this link.

The table pane is represented by the following API interfaces; ITablePane, ITablePaneEx and IExternalTablePane depending upon the data hosted in the view.

ITablePaneEx and IExternalTablePane were introduced at 3.1, ITablePane was an existing interface which provided a few customization methods. Developers should use the ITablePaneEx and IExternalTablePane interfaces to obtain the TableView for access to the full set of table view capabilities.

   // find all the table panes (table panes hosting map data)
   var tablePanes = FrameworkApplication.Panes.OfType<ITablePane>();   
   // find all the external table panes (table panes hosting external data)
   var externalPanes = FrameworkApplication.Panes.OfType<IExternalTablePane>();

   TableView tv = null;
   var firstTablePane = tablePanes.FirstOrDefault();
   var firstExternalTablePane = externalPanes.FirstOrDefault();
   if (firstTablePane != null)
   {
     var firstTablePaneEx = firstTablePane as ITablePaneEx;
     // change the caption
     firstTablePaneEx.Caption = "My table pane";
     tv = firstTablePaneEx.TableView;
   }
   else if (firstExternalTablePane != null)
   {
     firstExternalTablePane.Caption = "My table pane";
     tv = firstExternalTablePane.TableView;
   }

   if (tv == null)
     return;

   // set the zoom level
   tv.SetZoomLevel(200);

   // set some frozen fields
   var frozenFields = tv .GetFrozenFields();
   if (!frozenFields.Contains("CITY_FIPS"))
   {
     await tv.ClearAllFrozenFieldsAsync();

     await tv.SetFrozenFieldsAsync(new List<string> { "CITY_FIPS", "STATE_FIPS" });
     // adds to frozen
     await tv.SetFrozenFieldsAsync(new List<string> { "STATE_NAME" });
   }

MapTool

ArcGIS.Desktop.Mapping.MapTool is an abstract base class that provides a basic implementation of ArcGIS.Desktop.Framework.Contracts.Tool and represents a tool command used to perform interactive operations on a MapView. It provides virtual methods that can be overridden to perform actions when keyboard and mouse events occur in the map view. It also provides properties that can be set to configure the behavior of the tool, as well as methods that wrap common functions when interacting with the view. This base class will be used to create tools to interactively identify and select features in the view as well as when creating custom construction and editing tools.

Declaring a Map Tool

Map tools are added to the <controls> section of the add-in Config.daml. Same as buttons, map tools should be referenced within the groups that will contain them and the groups are, themselves, referenced within the individual tabs on which they should be shown. They have a corresponding code behind file which contains the map tool "class" implementation and it should also be referenced in the tool daml. For example:

<modules>
    <insertModule id="..." ... caption="Module1">
      <tabs>
         <tab id="ProAppModule1_Tab1" caption="New Tab">
          <group refID="ProAppModule1_Group1"/><!-- reference the group here -->
        </tab>
      </tabs>
      <groups>
        <group id="ProAppModule1_Group1" caption="Group 1">
          <tool refID="ProAppModule1_MapTool1" size="large" /><!-- reference the tool in the group(s) -->
        </group>
      </groups>
      <controls>
        <!-- define the tool element -->
        <tool id="ProAppModule1_MapTool1" caption="MapTool 1" className="MapTool1" ... />
      </controls>
    </insertModule>
  </modules>

This is the map tool code behind:

///<summary>MapTool1 derives from the class. Note: MapTool1 is referenced in the className attribute of
///its &lt;tool&gt; element in the Config.daml</summary>
internal class MapTool1 : MapTool 
{
  public MapTool1() 
  {
    ...
  }
  ...
}

The Pro SDK includes the ArcGIS Pro Map Tool template which automates much of the daml and code behind class file definition required to create a basic map tool. Simply run the Pro SDK map tool template to auto-generate the tool daml and class file definition.

3MapTool.png

Using Conditions

Enabled/Disabled status of a map tool can be controlled by adding a condition attribute on the tool daml element. The condition can be:

If the condition of a tool is set to be the daml id of a pane then only when instances of that type of pane are activated will your tool be enabled. The default condition for a map tool is set by the SDK Map Tool template as condition="esri_mapping_mapPane" where esri_mapping_mapPane is the daml id for the Pro MapView pane element.

<controls>
        <!-- Enable our tool whenever a 2D or 3D map view is active. Disable it otherwise -->
        <tool id="ProAppModule1_MapTool1" ... condition="esri_mapping_mapPane">
          <tooltip .../>
        </tool>

Mouse and keyboard events

MapTool provides several virtual methods such as OnToolMouseDown and OnToolKeyDown that are called when the corresponding event occurs in the map view. By overriding these methods in the derived class, you can perform actions when these events occur. The mouse events provide event arguments that contain information such as which mouse button was used, as well as the ClientPoint, relative to the top left corner of the map view, where the event occurred. The client point can be passed into the ClientToMap method to return the corresponding location in the map. The keyboard events provide event argument that provides information such as which key was used.

These virtual methods are intended to perform synchronous operations. If you need to execute any asynchronous code, you should set the handled property on the event arguments to true. By doing this, the corresponding Handle...Async virtual method will be called with the same event arguments. For example, to perform an asynchronous operation when the left mouse button is pressed, you would do the following:

protected override void OnToolMouseDown(MapViewMouseButtonEventArgs  e)
{
  if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
    e.Handled = true; //Handle the event to get the call to the async method
}

protected override Task HandleMouseDownAsync(MapViewMouseButtonEventArgs e)
{
  return QueuedTask.Run(() =>
  {
    var mapPoint = MapView.Active.ClientToMap(e.ClientPoint);
    ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(string.Format("X: {0} Y: {1} Z: {2}",
                   mapPoint.X, mapPoint.Y, mapPoint.Z), "Map Coordinates");
  });
}

Creating a sketch

By setting the IsSketchTool property to true, the tool will create a sketch in the map view with left mouse clicks. The geometry type of the sketch can be set using the SketchType property, and the SketchOutputMode property can be used to specify whether the sketch geometry will be created in screen or map coordinates. If you want the tool to snap while sketching set the UseSnapping property to true. If the IsSketchTool property is set to false then activating your map tool leaves the viewer in its default mode and clicks on the map will not create a sketch. If you have coded a map tool and are trying to figure out why it doesn't start sketching on the left mouse click then you may have forgotten to set the IsSketchTool property to true.

internal class MapTool1 : MapTool 
{
  public MapTool1() 
  {
    IsSketchTool = true;//Activate sketching when we are activated
    ...
  }

The "in progress" sketch is passed to the map tool via the protected OnSketchModifiedAsync() method. When the sketch is finished, the virtual method OnSketchCompleteAsync will be called, and it will pass in the geometry that was created by the sketch. Both of these methods can be overridden in your map tool to implement any required custom behavior.

This code snippet shows the basic outline of a map tool:

internal class MapTool1 : MapTool 
{
  public MapTool1() 
  {
    IsSketchTool = true;//Activate sketching when we are activated
    SketchType = SketchGeometryType.Rectangle;
    SketchOutputMode =  SketchOutputMode.Map; //use SketchOutputMode.Screen for 3D selection;
  }

  protected override Task OnToolActivateAsync(bool hasMapViewChanged) 
  {
    //TODO logic for when we are activated
  }

  protected override Task OnToolDeactivateAsync(bool hasMapViewChanged) 
  {
    //TODO logic for when we are deactivated
  }

  protected async override Task<bool> OnSketchModifiedAsync() 
  {
    //TODO - the sketch has been modified on the MapView
    //For example, access the sketch
    var sketch = this.ActiveMapView.GetCurrentSketchAsync();
    //Do something...
    return true;
  }

  protected override Task<bool> OnSketchCompleteAsync(Geometry geometry) 
  {
    //TODO - work with the sketch
  }
}

When performing an interactive selection or identify using the SelectFeatures or GetFeatures method, it's important to note that only geometries in screen coordinates are supported in 3D. In 2D, both map and screen geometries are supported.

Active Map Tool

Map tools in Pro are closely coupled with Map Panes (map pane being the "container" of an individual map view). A default tool can be assigned when a pane is declared in the daml. The default tool will be automatically selected when an instance of the pane is created. When working with multiple panes and moving between them, the active tool may change automatically to reflect the pane that is being activated. When returning to the original pane, the system will try to re-activate the most recently used tool for that pane type. Here is an excerpt from the ADMapping.daml which shows the assignment of the Pro explore tool as the map pane default.

<panes>
  <pane id="esri_mapping_mapPane" caption="Map" ... defaultTool="esri_mapping_exploreTool">
      ...
  </pane>

Animation

Animation is the process of creating a collection of sequential images and playing them back quickly to create an illusion of movement. Each image, just like a picture you take with a camera, marks a significant instance in time, and is called a keyframe. In ArcGIS Pro, animations are authored by defining a set of keyframes that the system uses to create interpolated intermediate frames. Animations can interpolate the camera position, map time, map range, and layer visibility and transparency.

The animations for a map can be accessed via the Animation property. This property is an Animation class and stores a collection of tracks which are containers for the keyframes. The tracks are returned from the Tracks property and this property always returns 4 tracks: a CameraTrack, a TimeTrack, a RangeTrack and a LayerTrack. Each track stores a collection of the corresponding keyframe type: CameraKeyframe, TimeKeyframe, RangeKeyframe, and LayerKeyframe. The track is used to get the collection of keyframes in addition to creating and deleting keyframes.

public List<CameraKeyframe> GetCameraKeyframes()
{
  var mapView = MapView.Active;
  if (mapView != null)
    return null;

  var animation = mapView.Map.Animation;
  //There will always be only 1 CameraTrack in the animation.
  var cameraTrack = animation.Tracks.OfType<CameraTrack>().First(); 
  return cameraTrack.Keyframes.OfType<CameraKeyframe>().ToList();
}

A Keyframe is a visual way-point along the animation path in a map or scene. When an animation is played, values such as the location of the camera, the current map time, the current map range, and layer transparencies are interpolated between the stored states defined by the keyframe using a configurable transition type. The keyframe stores one or more values, a transition for each value and the time it exists in the track. The transition type defines how the value is interpolated from the corresponding value in the previous keyframe to its value.

For example, let's say we have 2 keyframes in a track one at a time of 0 seconds and the second at a time of 3 seconds. The first keyframe has a value for X of 1 and the second keyframe has a value for X of 5. The second keyframe defines the transition for X as Linear. What this means is at 0 seconds in the animation X is 1, at 3 seconds X is 5 and at 1.5 seconds or halfway between the value of X would be 3.

If we apply this example to the Camera which has several values including X, Y, Z, Pitch, Heading and Roll that define the viewing location and viewing rotation the CameraKeyframe will store all of these values in addition to a transition for each value. These values and transitions define the path the camera moves during the animation.

MapControl

MapControl is a reusable control that can be embedded inside a dock pane, view pane, or a dialog and used to display project items such as 2D and 3D maps, feature layers, raster datasets, layer packages etc. The snippet below shows how you can add a map control in XAML:

<mapControls:MapControl Name="mapControl" Grid.Row="1" 
              VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="275"
              ViewContent="{Binding Path=MapContent}" 
              Camera="{Binding CameraProperty}">
</mapControls:MapControl>

As shown in the snippet above, you bind to the map control's ViewContent property to define the content currently displayed in the control and the Camera property to navigate inside the map control. You can also bind to the Extent property to control the map control's extent.

The ViewContent is of type MapControlContent. The static MapControlContentFactory class is used to create the map control content. The IsItemSupported method on the MapControlContentFactory class can be used to determine whether a particular item can be added to the map control. If an item is supported then you can use the various overloads of the MapControlContentFactory's Create method to create the map control content.

The example below shows how to create map control content from the currently selected item in the Project pane or view:

private MapControlContent CreateContentFromItem()
{
   var curProj = Project.Current;
   var selectedItem = ((curProj != null) && (curProj.SelectedItems.Count == 1)) ? curProj.SelectedItems[0] : null;

   if (selectedItem == null || !MapControlContentFactory.IsItemSupported(selectedItem))
      return null;

   Camera camera = null;
   return MapControlContentFactory.Create(new[] { selectedItem }, camera, ArcGIS.Core.CIM.MapViewingMode.Map);
}

This is an example of how you can create map control content from the currently active map in your project:

var currentMap = MapView.Active.Map;
MapControlContent _mapContent = MapControlContentFactory.Create(
      currentMap, MapView.Active.Extent, currentMap.DefaultViewingMode);

Other properties on the map control include the IsReady read-only property. This can be queried to find out whether the map control is ready to accept user input. For example, before updating the map control camera you can perform the following check:

if (MapControlWindow._mapControl.IsReady)
{
  //Update the map control camera
}

You can also bind to the IsReady property in xaml to enable or disable other controls. The snippet below shows how a button's enabled state can be bound to the map control's IsReady property:

<Button  Name="TestButton" DockPanel.Dock="Right" Style="{StaticResource buttonStyle}" 
         Click="OnButtonClick" 
         IsEnabled="{Binding Path=IsReady, ElementName=mapControl}">            
</Button>

Similar to the IsReady property is the read-only IsDrawing property that can be queried to determine whether the control is currently drawing the view content. An example usage of the IsDrawing property would be to show a spinner for user feedback while the content is being drawn.

NOTE - While you can bind another control's property to the map control's IsReady or IsDrawing properties as shown in the XAML snippet above, you cannot bind these properties as follows - the XAML below will give an error as IsReady is a read-only dependency property:

<mapControls:MapControl Name="mapControl" Grid.Row="1" 
              VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="275"
              ViewContent="{Binding Path=MapContent}" 
              Camera="{Binding CameraProperty}"
                
              IsReady ="{Binding MapControlIsReady}>

</mapControls:MapControl>

There is also an OverlayControl property that can be used to display an overlay like an image, text etc. inside the map control. The XAML snippet below shows an example where some text is shown in the center of the map control:

<mapControls:MapControl.OverlayControl>
  <Grid>
    <TextBlock TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center">
     This is a simple example of using the <Bold>OverlayControl</Bold> to draw text in the map control. 
    </TextBlock>
  </Grid>
</mapControls:MapControl.OverlayControl>

NOTE - The MapControl is a native control and if it is placed in a transparent native window then the MapControl will also be transparent and will not display. An example of this is if you use the MapControl as an embeddable control by associating it with a MapTool then the MapControl will be hosted in a transparent native window and will not be visible.

The map control raises a number of events which you can hook into to obtain information. The ControlInitialized event is raised when it is initialized and ready to receive user input. Other events are ViewContentLoaded, DrawStarted, DrawComplete, CameraChanged and ExtentChanged. Specific actions can be performed by handling these events.

Samples using the Map control can be found at OverviewMapControl and MagnifierWindow

TableControl

The TableControl is a reusable control that can be embedded inside a pane, dock pane or dialog and used to display content of a map member from an active map or an item from the Catalog view. The snippet below shows how you can add a table control in XAML: (Note that the TableControl is found in the ArcGIS.Desktop.Editing.Controls namespace.)

<editingControls:TableControl AutomationProperties.AutomationId="_tableControl" 
                      x:Name="tableControl" VerticalAlignment="Stretch"
                      HorizontalAlignment="Stretch"
                      TableContent="{Binding Path=TableContent}">
</editingControls:TableControl>

You bind a table control's TableContent property to define the content displayed in the control. This property is of type TableControlContent which is created using the static TableControlContentFactory class. The IsMapMemberSupported or IsItemSupported functions on the TableControlContentFactory determine if content can be created from the mapMember or Catalog item and displayed in the table control.

The example below creates table control content from a supported map member item:

private TableControlContent CreateTableControlContent()
{
   var selectedLyrs = MapView.Active.GetSelectedLayers();
   var selectedSTs = MapView.Active.GetSelectedStandaloneTables();
   MapMember item = null;

   if (selectedLyrs != null && selectedLyrs.Count == 1)
   {
     item = selectedLyrs[0];
   }
   else if (selectedSTs != null && selectedSTs.Count == 1)
   {
     item = selectedSTs[0];
   }

   if ((item != null) && TableControlContentFactory.IsMapMemberSupported(item))
     return TableControlContentFactory.Create(item);

   return null;
}

This is an example of creating table control content from the selected item in the Catalog pane.

private TableControlContent CreateTableControlContentFromItem()
{
  var catalogPane = Project.GetCatalogPane();
  var item = catalogPane.SelectedItems.FirstOrDefault();
  if ((item != null) && TableControlContentFactory.IsItemSupported(item))
    return TableControlContentFactory.Create(item);

  return null;
}

The static TableControlCommands class provides a number of commands for use with the table control. Here is a snippet showing how to define them in xaml. Take particular notice of the CommandTarget property. It is necessary to reference the table control's Name property, otherwise the commands do not have the correct context and will not execute.

<Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <DockPanel LastChildFill="False" Grid.Row="0">
      <Button Content="Toggle Sel" Command="editingControls:TableControlCommands.SwitchSelection"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>

      <Button Content="Select All" Command="editingControls:TableControlCommands.SelectAll"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>

      <Button Content="Clear Sel" Command="editingControls:TableControlCommands.ClearSelection"
              CommandTarget="{Binding ElementName=tableControl}"
              Style="{DynamicResource Esri_Button}"/>
    </DockPanel>
    <editingControls:TableControl AutomationProperties.AutomationId="_tableControl" x:Name="tableControl" 
                       Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                       TableContent="{Binding Path=TableContent}" >
    </editingControls:TableControl>
  </Grid>

The functionality exposed by the commands in TableControlCommands is also available as methods on the table control class itself. For example there are CanSwitchSelection and SwitchSelection, CanClearSelection and ClearSelection, and CanSelectAll and SelectAll methods amongst others. Use these within your ViewModel or code-behind if you need to combine them with additional functionalities.

Additional functionalities added at 3.1 allow access to manipulate the highlighted rows when the table is in Selected records mode. Use CanHighlight and Highlight, CanSwitchHighlight and SwitchHighlight, CanToggleHighlight and ToggleRowHighlight, CanClearHighlighted and ClearHighlighted. Retrieve the set of highlighted rows with GetHighlightedObjectIds

You can also display the Go To UI using the CanGoTo and GoTo methods in code or the static TableControlCommands.GoTo property in xaml. Similarly the Find UI can be displayed via CanFind and Find methods in code or the static TableControlCommands.Find in xaml. Note that only the Find UI can be displayed; the Find And Replace UI is not appropriate as the table control displays data in read-only mode.

Other important properties on the TableControl include ViewMode which allows you to toggle the control in either All Records or Selected Records mode; ActiveFieldIndex which sets the active field of the TableControl, along with ActiveRowIndex which sets the active row.

You can also customize the row or column context menus by binding to the RowContextMenu, SelectedRowContextMenu and ColumnContextMenu properties.

The IsRowCountKnown property determines if the control knows the row count of the data source. This read-only property can be used as a check before accessing the RowCount property or selecting all rows.

The Table control primarily operates using row indexes. You can modify the current row by changing the ActiveRowIndex. Alternatively you can use the BringIntoView method to modify the current row and ensure that the table also scrolls to that row. In many cases however, you may only know the ObjectID of the row that you wish to make current. Use the GetRowIndex or GetRowIndexAsync methods to determine the row index for a given ObjectID in the dataset. If you wish to know the ObjectID at a particular row index you can use the GetObjectIdAsync method.

At 3.1, you can also control field display by toggling between showing field alias or field name, hiding a set of fields, freezing fields, setting field order, or sorting the table.

The Table control raises a number of events which you can hook into to obtain information. Use TableContentLoaded to determine when the table content has been loaded, ActiveCellChanged to determine when the active cell has been changed or SelectedRowsChanged to determine when the selected rows have changed.

A sample using the Table control can be found at TableControl. The ProGuide TableControl provides additional documentation and accompanies the sample.

MapView Graphic Overlay

Temporary graphics can be added to your map view or scene view using a "graphic overlay" associated with the map or scene views. The overlays are for showing temporary data that's not persisted when the view is closed. Overlay graphics are defined using a geometry to specify its location and a corresponding CIM symbol (technically a CIM symbol symbol reference). A CIMGraphic can also be used (which combines the geometry and symbol into a single entity). Graphics can be points, lines, polygon, or text (to include callouts).

In the image below, a point geometry symbolized with a star has been added to the overlay. Additionally, a second text graphic has been added, using a callout, to "add" a label to the star.

graphicOverlay

To create a Graphic overlay, use one of the MapView AddOverlay methods. There are multiple overloads for this method that use a geometry and a Symbol reference; a CIMGraphic; or a feature oid (for the location) and Symbol Reference.

public static IDisposable AddOverlay(MapView mapView, Geometry geometry, CIMSymbolReference symbol, 
                                     double referenceScale = -1)
public static IDisposable AddOverlay(MapView mapView, Geometry geometry, CIMSymbolReference symbol, 
                                     double referenceScale, double showThrough)
public static IDisposable AddOverlay(MapView mapView, CIMGraphic graphic, 
                                     double referenceScale = -1)
public static IDisposable AddOverlay(MapView mapView, CIMGraphic graphic, 
                                     double referenceScale, double showThrough)
public static IDisposable AddOverlay(MapView mapView, Layer layer, long oid, 
                                     CIMSymbolReference symbol = null, double referenceScale = -1)

In the methods above, the referenceScale is an optional parameter. The showThrough parameter is used in 3D only and is a visibility factor for showing the overlay through other objects when obscured.

The AddOverlay method returns an IDisposable which can be used to clear the graphic. To remove the graphic, call "Dispose" on the returned IDisposable (eg _graphic.Dispose()). Save the IDisposable in a member variable if you need to maintain a reference to it. If you do not remove the graphic, it will automatically be removed when the mapview is closed.

The following code snippet illustrates how to create a graphic using a geometry and CIM symbol (reference).

  private IDisposable _graphic = null; //defined elsewhere

  // Add point graphic to the overlay at the center of the mapView
  // Note: Run within QueuedTask.Run
  _graphic = mapView.AddOverlay(mapView.Extent.Center, 
               SymbolFactory.Instance.ConstructPointSymbol(
                 ColorFactory.Instance.RedRGB, 40.0, SimpleMarkerStyle.Star).MakeSymbolReference());

  // Note: Update graphic using UpdateOverlay
  // mapView.UpdateOverlay(_graphic,...);

  _graphic.Dispose(); //Removes the graphic

Sample: OverlayExamples
ProSnippet: Graphic Overlay
ProSnippet: Graphic Overlay with CIMPictureGraphic
ProSnippet: Add overlay graphic with text

MapView Overlay Control

WPF User Controls can also be added "on top of" the mapview or scene using a MapViewOverlayControl wrapper passed as the parameter to mapView.AddOverlayControl(...).

MapViewOverlayControl

To add a user control on top of Pro's mapview, first create a MapViewOverlayControl. The first parameter in the MapViewOverlayControl constructor is the WPF user control that you want to display on top of the map view. The MapViewOverlayControl constructor has parameters to configure various aspects of your user control's display and behavior such as its initial position, and whether it can be moved or resized, etc.

public MapViewOverlayControl( 
   FrameworkElement control,
   bool canMove,
   bool canResizeHorizontally,
   bool canResizeVertically,
   OverlayControlRelativePosition relativePosition,
   double initialXRatio,
   double initialYRatio
)

You can create a simple WPF User Control xaml (in this case two text blocks surrounded with a maroon border):

<UserControl x:Class="OverlayControlExample.OverlayTestingControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             ...>
    <Grid>
      <Border Background="Beige" BorderBrush="Maroon" BorderThickness="4">
        <StackPanel Orientation="Vertical" >
          <TextBlock FontSize="20" FontWeight="Bold" Margin="50, 10"> MapView Overlay Control</TextBlock>
          <TextBlock FontSize="20" FontWeight="Bold" Margin="50, 20"><Run Text="Insert controls here."/>
          </TextBlock>
        </StackPanel>
      </Border>            
    </Grid>
</UserControl>

To add our custom user control to the mapview overlay, we pass an instance of our user control to the MapViewOverlayControl constructor along with the desired placement settings. We then invoke MapView.AddOverlayControl to add our user control to the mapview overlay:

//instantiates the control
var exampleControl = new OverlayTestingControl();

//allow it to be moved and resized
var MapViewOverlayControl = new MapViewOverlayControl(exampleControl, true, true, true, 
                                    OverlayControlRelativePosition.TopLeft);

//add the wrapper to the overlay        
mapView.AddOverlayControl(MapViewOverlayControl);

An alternative to creating a WPF control is to create an Embeddable Control; this gives you a view and a view model pair. This may be useful if the UI is more complex and requires data binding to update information on the UI. An embeddable control is registered in the daml in the esri_embeddableControls category and has a unique ID.

  <categories>
    <updateCategory refID="esri_embeddableControls">
      <insertComponent id="OverlayControlExample_EmbeddableControl1" className="EmbeddableControl1ViewModel">
        <content className="EmbeddableControl1View" />
      </insertComponent>
    </updateCategory>
  </categories>

This daml identifer can be passed to the EmbeddableControl.Create method to create the view and view model pair. The code for adding the embeddable control to the overlay is as follows

//instantiates the embeddable control and sets datacontext
var tuple = EmbeddableControl.Create("OverlayControlExample_EmbeddableControl1");
//Get the user control (view)
var exampleControl = tuple.Item2 as EmbeddableControl1View;

//allow it to be moved and resized
var MapViewOverlayControl = new MapViewOverlayControl(
           exampleControl, true, true, true, OverlayControlRelativePosition.TopLeft);

//add the wrapper to the overlay        
mapView.AddOverlayControl(MapViewOverlayControl);

The MapViewOverlayControl object should be held in a member variable if access is required later, or to send to RemoveOverlayControl when it is no longer needed (and should be removed from the overlay). If RemoveOverlayControl is not called, the overlay control is removed automatically when the mapview is closed.

Sample: ScribbleControl_ArcGISPro

ProSnippet: MapView Overlay Control

Table Options

Table options corresponding to the table options on the Pro options UI are available in the API via the static ApplicationOptions.TableOptions property - refer to ProConcepts Content - ApplicationOptions for more information. The available options include:

  // Gets and sets the application table options.
  public class TableOptions
  {
    // Gets and sets whether the "Click to add new row" option is hidden for feature
    // class tables.
    public bool HideAddNewRow { get; set; }
    // Gets and sets whether selection color overrides are honored.
    public bool HonorSelectionColorOverrides { get; set; }
    // Gets the default highlight color.
    public CIMColor DefaultHighlightColor { get; }
    // Gets the highlight color.
    public CIMColor HighlightColor { get; }
    // Get the default font name.
    public string DefaultFontName { get; }
    // Gets the default font size.
    public double DefaultFontSize { get; }
    // Gets and sets the font name.
    public string FontName { get; set; }
    // Gets and sets the font size.
    public double FontSize { get; set; }
    // Gets and sets the column header height type
    public TableRowHeightType ColumnHeaderHeightType { get; set; }
    // Gets and sets the row height type.
    public TableRowHeightType RowHeightType { get; set; }
    // Gets and sets if the mapview is to be activated after using Flash, Pan or Zoom
    // from the table.
    public bool ActivateMapViewAfterOperations { get; set; }

    // Determines if a specified font name is valid for use in the TableOptions.
    public bool IsValidFontName(string fontName);
    // Determines if a specified font size is valid for use in the TableOptions.
    public bool IsValidFontSize(double fontSize);
    // Sets the highlight color. This method must be called on the MCT. Use QueuedTask.Run.
    public void SetHighlightColor(CIMColor highlightColor);
  }

Example:

      var options = ApplicationOptions.TableOptions;

      var hideAddNewRow = options.HideAddNewRow;
      options.HideAddNewRow = !hideAddNewRow;

      var overrides = options.HonorSelectionColorOverrides;
      options.HonorSelectionColorOverrides = !overrides;

      var activateMapView = options.ActivateMapViewAfterOperations;
      options.ActivateMapViewAfterOperations = !activateMapView;

      var defaultFontTName = options.DefaultFontName;
      var fontName = options.FontName;
      if (options.IsValidFontName("Arial"))
        options.FontName = "Arial";

      var defaultFontSize = options.DefaultFontSize;
      var fontSize = options.FontSize;
      if (options.IsValidFontSize(10))
        options.FontSize = 10;

      var heightType = options.ColumnHeaderHeightType;
      options.ColumnHeaderHeightType = TableRowHeightType.Double;

      var rowHeightType = options.RowHeightType;
      options.RowHeightType = TableRowHeightType.Single;

      var defaultColor = options.DefaultHighlightColor;
      var color = options.HighlightColor;
      QueuedTask.Run(() =>
      {
        options.SetHighlightColor(ColorFactory.Instance.CreateRGBColor(0, 0, 255));
      });
⚠️ **GitHub.com Fallback** ⚠️