Skip to content

ProConcepts Content and Items

UmaHarano edited this page Nov 6, 2023 · 18 revisions

This ProConcepts will cover content in Pro, its usage, and relevant API functionality.

Language:      C#
Subject:       Content
Contributor:   ArcGIS Pro SDK Team <arcgisprosdk@esri.com>
Organization:  Esri, http://www.esri.com
Date:          10/02/2023  
ArcGIS Pro:    3.2  
Visual Studio: 2022

In this topic

Overview

In Pro, content is represented as Items. There are two categories of Items:

  • Content that is stored in, or referenced by, the Project document. This content, is referred to as project content (or project "items")* and includes maps, layouts, styles, toolboxes, folders, etc.
  • Content that is browsed by Pro and is external to the project. Content can be browsed from folders, portal and online, geodatabases, and toolboxes and includes text files, xml files, folders, database connection files, layer files, task files, mxds, sxds, etc. This content, is referred to as external content (or external "items").

A complete list of the supported data types and items for Pro can be found here

*Content can also be included within Favorites at 2.0.

Project Content

Project content, or project "items", lives within the project. At some point in time, project content was either added or imported into the project from an external source or was created (and saved) in a Pro session. Once in a project, content can packaged and shared (see Project Package)

In the API, project "items" all include the suffix ProjectItem in their class name and derive from ArcGIS.Desktop.Core.Item (by way of a couple of internal classes not relevant to the public API). Project item classes implement the interface IProjectItem, topic 15851, which is the required parameter type for adding and removing content to and from Pro: Project.Current.AddItem(IProjectItem item), topic 15947, and Project.Current.RemoveItem(IProjectItem item), topic 15951. Adding content to the project is discussed in detail in ItemFactory and AddItem.

The public API currently provides the following project items:

https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/.html

Class Namespace Assembly
BusinessAnalystProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
FolderConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
GDBProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
HuffModelProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
LocatorsConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
SegmentationProfileProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
SegmentationProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
SegmentationTargetGroupProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
ServerConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
StatisticalDataCollectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
VariableListProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
ReviewerBatchJobProjectItem ArcGIS.Desktop.DataReviewer ArcGIS.Desktop.DataReviewer.dll
ReviewerResultsProjectItem ArcGIS.Desktop.DataReviewer ArcGIS.Desktop.DataReviewer.dll
GeoprocessingProjectItem ArcGIS.Desktop.GeoProcessing ArcGIS.Desktop.GeoProcessing.dll
SAModelsProjectItem ArcGIS.Desktop.GeoProcessing ArcGIS.Desktop.GeoProcessing.dll
LayoutProjectItem ArcGIS.Desktop.Layouts ArcGIS.Desktop.Layouts.dll
ReportProjectItem ArcGIS.Desktop.Layouts ArcGIS.Desktop.Layouts.dll
MapProjectItem ArcGIS.Desktop.Mapping ArcGIS.Desktop.Mapping.dll
StyleProjectItem ArcGIS.Desktop.Mapping ArcGIS.Desktop.Mapping.dll
TaskProjectItem ArcGIS.Desktop.TaskAssistant ArcGIS.Desktop.TaskAssistant.dll

Project Units

Application units in Pro are represented by the ArcGIS.Desktop.Core.UnitFormats.DisplayUnitFormat class. The class contains both a measurement unit, displayUnitFormat.MeasurementUnit, for calculating measurements and a displayUnitFormat.UnitFormatType which controls how unit values are formatted. A DisplayUnitFormat can have one of 8 different UnitFormatTypes matching the same organization of units into 8 sub categories as on the Project options "units" dialog. The first five unit unit formats are geographic measures that are used to describe locations on the earth, while the last three are page and symbol measures that refer to the placement of objects on a page:

  • UnitFormatType.Distance: Distance units specify distances between locations.
  • UnitFormatType.Angular: Angular units specify distances on a sphere or spheroid.
  • UnitFormatType.Area: Area units measure the size of two-dimensional planes.
  • UnitFormatType.Location: Location units describe an absolute geographic location in x,y format. They are related to the coordinate system of the project.
  • UnitFormatType.Direction: Direction units reference a meridian to describe the spatial relationship between a direction and a reference axis.
  • UnitFormatType.Page: Page units, usually millimeters or inches, are used to arrange map elements on a layout page for printing.*
  • UnitFormatType.Symbol2D: Symbol2D units are used to render dimensions of shapes, distance tolerances, and offsets both on a computer screen and on a printed map.*
  • UnitFormatType.Symbol3D: Symbol3D units are used to render dimensions of shapes, distance tolerances, and offsets for three-dimensional symbols.**

The list of available units of a given UnitFormatType, currently available in the current project, can be accessed via DisplayUnitFormats.Instance.GetProjectUnitFormats(unit_type) and the full list of all possible units, whether available in the current project or not, is accessed off DisplayUnitFormats.Instance.GetPredefinedProjectUnitFormats(unit_type). The returned collections correspond to the list of units shown on the Project options unit dialog and the full list of units associated with each unit type, also on the Project options dialog, respectively.

*The list of available units is fixed and cannot be changed.

**The list of available Symbol3D units corresponds to the list of available UnitFormatType.Distance units. Changes made to the list of distance units automatically appear in the list of 3D symbol display unit formats.

Project unit format collections can always be accessed, even when a project is not open, however, changes made to the different collections of project unit formats can only be persisted if a project is currently open.

In this example, both the full list of all available units and current list of units in the project, is retrieved (for all UnitFormatTypes) from the DisplayUnitFormats singleton instance:

  QueuedTask.Run(() => {
    var unit_formats = Enum.GetValues(typeof(UnitFormatType)).OfType<UnitFormatType>().ToList();
   
    //The full list of unit formats
    System.Diagnostics.Debug.WriteLine("Full list\r\n");
    foreach(var unit_type in unit_formats) {
       System.Diagnostics.Debug.WriteLine($"Format: {unit_type.ToString()}");
       var units = DisplayUnitFormats.Instance.GetPredefinedProjectUnitFormats(unit_type);
       foreach(var unit in units) {
          var line = $"{unit.DisplayName}, {unit.UnitName}, {unit.UnitCode}:: {unit.MeasurementUnit.Name}";
          System.Diagnostics.Debug.WriteLine(line);
       }
    }

   System.Diagnostics.Debug.WriteLine("\r\nProject list\r\n");
    foreach(var unit_type in unit_formats) {
       System.Diagnostics.Debug.WriteLine($"Format: {unit_type.ToString()}");
       var units = DisplayUnitFormats.Instance.GetProjectUnitFormats(unit_type);
       foreach(var unit in units) {
          var line = $"{unit.DisplayName}, {unit.UnitName}, {unit.UnitCode}:: {unit.MeasurementUnit.Name}";
          System.Diagnostics.Debug.WriteLine(line);
       }
    }
    ...

The list of which unit format choices are available for the current project can be updated via SetProjectUnitFormats(replacement_list, replacement_default) where "replacement_default" can be null. This is a complete replacement (not an add or update). Whatever entries are in the replacement list are set as the new list of available choices (accessed off the GetProjectUnitFormats(...) method). If a replacement default unit format is not specified (i.e. the replacement default parameter is null) then the existing default unit is retained. The entries in the "replacement list" must all be of the same UnitFormatType or an ArgumentException will be thrown. Attempting to change the list of units for Page, Symbol2D, and Symbol3D* will throw an InvalidOperationException.

 //Must be on the QueuedTask.Run()
 //Get the full list of distance units
 var all_units = DisplayUnitFormats.Instance.GetPredefinedProjectUnitFormats(UnitFormatType.Distance);
 //keep units with an even factory code
 var list_units = all_units.Where(du => du.UnitCode % 2 == 0).ToList();
 //set them as the new distance unit collection. A new default is not being specified...
 DisplayUnitFormats.Instance.SetProjectUnitFormats(list_units);
 

*Recall that the list of units for Page and Symbol2D are fixed and the list of available UnitFormatType.Symbol3D units is changed via the list of UnitFormatType.Distance units.

The currently selected default unit format for the given UnitFormatType can be accessed via DisplayUnitFormats.Instance.GetDefaultProjectUnitFormat(unit_type) and can be changed via DisplayUnitFormats.Instance.SetDefaultProjectUnitFormat(displayFormatUnit). If a project is not currently open when SetDefaultProjectUnitFormat(...) is called, the change will be ignored. In this example, the list of current project default format units is retrieved and the default of each list is set to the last entry in each respective list (whether it is the current default or not):

 QueuedTask.Run(() => {
    var unit_formats = Enum.GetValues(typeof(UnitFormatType)).OfType<UnitFormatType>().ToList();
    foreach(var unit_type in unit_formats) {
       var current_default = DisplayUnitFormats.Instance.GetDefaultProjectUnitFormat(unit_type);
       //Arbitrarily pick the last unit in each unit format list
       var replacement = DisplayUnitFormats.Instance.GetProjectUnitFormats(unit_type).Last();
       DisplayUnitFormats.Instance.SetDefaultProjectUnitFormat(replacement);

       var line = $"{current_default.DisplayName}, {current_default.UnitName}, {current_default.UnitCode}";
       var line2 = $"{replacement.DisplayName}, {replacement.UnitName}, {replacement.UnitCode}";

       System.Diagnostics.Debug.WriteLine($"Format: {unit_type.ToString()}");
       System.Diagnostics.Debug.WriteLine($" Current default: {line}");
       System.Diagnostics.Debug.WriteLine($" Replacement default: {line2}");

    }

Changes can be made to individual DisplayUnitFormat instances to customize their properties (such as name, display name, abbreviation, and format parameters) Changes made to individual DisplayUnitFormat instances must be applied back to the project unit format collection, via SetProjectUnitFormats(...), in order for them to be persisted. The exception is the default format unit. In this case, just the default unit can be updated by passing in an updated DisplayUnitFormat instance to the SetDefaultProjectUnitFormat(...) method. Changes made to any of the list of units or to individual units themselves are persisted by saving the current project otherwise the changes will be lost when the project is closed. Changes made to DisplayUnitFormats are limited to just the project, or projects, within which changes are made.

In this example, the unit's abbreviation is appended to the end of each unit's display name in the list of UnitFormatType.Angular unit types and then applied back to the current project:

QueuedTask.Run(() => {
    var angle_units = DisplayUnitFormats.Instance.GetProjectUnitFormats(UnitFormatType.Angular);
    //Edit the display name of each unit
    foreach(var unit in angle_units) {
        unit.DisplayName = $"{unit.DisplayName} ({unit.Abbreviation})";
    }
    //apply the units and set the default to be the first entry
    DisplayUnitFormats.Instance.SetProjectUnitFormats(angle_units, angle_units.First());

    //The project must be saved to persist the changes...
    ...

Addins interested in changes to the project units should subscribe to the ArcGIS.Desktop.Core.Events.ProjectUnitFormatsChangedEvent event. Any change to the lists of project units fires this event (whether changes were made to the Project unit format lists, defaults, or to display unit format properties). The ProjectUnitFormatsChangedEventArgs instance contains a ProjectUnitFormatsChangedEventArgs.DefaultsChangedHint property with a list of the UnitFormatTypes for those default values that were changed.

Note that the DefaultsChangedHint will always contain a list of all UnitFormatTypes if any change was made to the units via the Project options units dialog. All current default units for the project get overrwritten on an 'Ok' triggering an event with all UnitFormatTypes added to the DefaultsChangedHint collection. Only if an add-in makes a specific change to a project unit default will the DefaultsChangedHint contain just the specific UnitFormatType (or Types) whose default(s) were changed.

 ArcGIS.Desktop.Core.Events.ProjectUnitFormatsChangedEvent.Subscribe((args) => {
    //A change was made to the project units via an add-in or from the Pro UI

    //Check the hint - NOte: DefaultsChangedHint will contain all 8 UnitFormatTypes if the event 
    //was triggered by an update off the Project options unit dialog
    foreach (var hint in args.DefaultsChangedHint) {
      //TODO - use the event hint as needed
    }
    ...
 });

Application Options

A subset of Pro application options are available via the ArcGIS.Desktop.Core.ApplicationOptions class. Available options in the API are organized via a series of "options" categories , or classes, accessed as static properties off ApplicationOptions which loosely correspond to the same organization (of options) on the Pro application "Options" dialog (accessed via the Pro backstage). ApplicationOptions was introduced at 2.9 and has been extended over subsequent releases. The following options categories/classes are available in the current release:

///<summary>Available Pro application backstage options</summary>
public static class ApplicationOptions {
  ///<summary>Gets the available application general options</summary>
  public static GeneralOptions GeneralOptions { get; }
  ///<summary>Gets the available application display options</summary>
  public static DisplayOptions DisplayOptions { get; }
  ///<summary>Gets the available application navigation options</summary>
  public static NavigationOptions NavigationOptions { get; }
  ///<summary>Gets the application download options</summary>
  public static DownloadOptions DownloadOptions { get; }
  ///<summary>Gets the available application geoprocessing options</summary>
  public static GeoprocessingOptions GeoprocessingOptions { get; }
  ///<summary>Gets the application layout options</summary>
  public static LayoutOptions LayoutOptions { get; }
  ///<summary>Gets the application text and graphics elements options</summary>
  public static TextAndGraphicsElementsOptions TextAndGraphicsElementsOptions { get; }
  ///<summary>Gets the application editing options</summary>
  public static EditingOptions EditingOptions { get; }
  ///<summary>Gets the application versioning options</summary>
  public static VersioningOptions VersioningOptions { get; }
}

The pattern of usage is the same for each given option category/class and follows the same behavior implemented on the Pro options UI. The available application settings (in each option category), correspond to individual "get" and "set" properties which are accessed off their respective, parent static option category class (which are, in turn, accessed from ApplicationOptions). Any change to a given setting is applied immediately, without a separate commit or save action. Properties set via the API are not undoable. To revert a property back to its previous value, you must execute another "set" to whichever is the desired value. Certain properties may implement internal validation logic to ensure that the value being "applied" or "set" is valid for the given setting. In most cases, attempting to set an invalid property value will be ignored.

*In some cases, a "Get" and "Set" method, rather than a property, may be provided. This is especially true when a particular setting can only be accessed and manipulated via the Pro Main CIM Thread, MCT (which requires a QueuedTask.Run).

At 3.1, functionality was introduced to ArcGIS Pro to allow system administrators to deploy the application across a team or an organization and control a number of settings on behalf of their users. Administrators can set default values for a select number of settings. They can also lock these settings so that users cannot change them from the value set by the administrator. Developers should note that if a particular setting is locked, then changing the value of the associated property via the ApplicationOptions category/class API is ignored.

In the following example, the add-in is accessing and setting ApplicationOptions.GeneralOptions for startup, geodatabase, and toolbox options:

  //access the current values
  var startMode = ApplicationOptions.GeneralOptions.StartupOption;
  var aprx_path = ApplicationOptions.GeneralOptions.StartupProjectPath;

  var gdb_option = ApplicationOptions.GeneralOptions.DefaultGeodatabaseOption;
  var def_gdb = ApplicationOptions.GeneralOptions.CustomDefaultGeodatabase;//can be null

  var tbx_option = ApplicationOptions.GeneralOptions.DefaultToolboxOption;
  var def_tbx = ApplicationOptions.GeneralOptions.CustomDefaultToolbox;//can be null

  //Set the application to use a custom project, gdb, and toolbox
  //In each case, the custom _path_ must be set _first_ before setting the "option"

  ApplicationOptions.GeneralOptions.StartupProjectPath = @"E:\data\usa.aprx";//custom project path first
  ApplicationOptions.GeneralOptions.StartupOption = StartProjectMode.WithDefaultProject;//option to use it second

  ApplicationOptions.GeneralOptions.CustomDefaultGeodatabase = @"E:\data\usa.gdb";//custom gdb path first
  ApplicationOptions.GeneralOptions.DefaultGeodatabaseOption = OptionSetting.UseCustom;//option to use it second

  ApplicationOptions.GeneralOptions.CustomDefaultToolbox =  @"E:\data\usa.tbx";//custom toolbox path first
  ApplicationOptions.GeneralOptions.DefaultToolboxOption = OptionSetting.UseCustom;//option to use it second

  //To set the paths back to _null_ (the default), the options must be set _back_ to
  //their default value _first_ (i.e. reverse of the order used to set the custom path
  //in the first place)
  ApplicationOptions.GeneralOptions.StartupOption = StartProjectMode.ShowStartPage;//set default option first
  ApplicationOptions.GeneralOptions.StartupProjectPath = null;//path back to null second*
  
  ApplicationOptions.GeneralOptions.DefaultGeodatabaseOption = OptionSetting.UseDefault;//set default option first
  ApplicationOptions.GeneralOptions.CustomDefaultGeodatabase = null;//path back to null second*
  
  ApplicationOptions.GeneralOptions.DefaultToolboxOption = OptionSetting.UseDefault;//set default option first
  ApplicationOptions.GeneralOptions.CustomDefaultToolbox =  null;//path back to null second*

  //nulling out the path isn't actually necessary to switch back to the default behavior. It is done really for
  //completeness. If the option setting is set to use "default", then any custom path that may (still) be stored 
  //in the companion project, gdb, toolbox path property will be ignored. However, the path(s) will be visible on
  //the General options UI.
  

In this example, the add-in is accessing and setting ApplicationOptions.TextAndGraphicsElementsOptions for default font, default point, line, and polygon symbols:

 //Note: QueuedTask required...
 QueuedTask.Run(()=> {

   //First, access the existing defaults for font, point, line, poly
   var def_font = ApplicationOptions.TextAndGraphicsElementsOptions.GetDefaultFont();//returns a tuple
   System.Diagnostics.Debug.WriteLine($"default font: {def_font.fontName}, {def_font.styleName}");
 
   //Current symbol defaults
   var ptSymbol = ApplicationOptions.TextAndGraphicsElementsOptions.GetDefaultPointSymbol();
   var lineSymbol = ApplicationOptions.TextAndGraphicsElementsOptions.GetDefaultLineSymbol();
   var polySymbol = ApplicationOptions.TextAndGraphicsElementsOptions.GetDefaultPolygonSymbol();
   var textSymbol = ApplicationOptions.TextAndGraphicsElementsOptions.GetDefaultTextSymbol();

  //Now set some new defaults
  ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultFont("tahoma");
  //...or with a style as well
  //ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultFont("tahoma", "bold");

  var ptSymbol2 = SymbolFactory.Instance.ConstructPointSymbol(
                    ColorFactory.Instance.RedRGB, 14, SimpleMarkerStyle.Diamond);
  var lineSymbol2 = SymbolFactory.Instance.ConstructLineSymbol(
                      ColorFactory.Instance.RedRGB, 2, SimpleLineStyle.Dash);
  var polySymbol2 = SymbolFactory.Instance.ConstructPolygonSymbol(
                      ColorFactory.Instance.RedRGB, SimpleFillStyle.DiagonalCross);
  var textSymbol2 = SymbolFactory.Instance.ConstructTextSymbol(
                      ColorFactory.Instance.RedRGB, 12);
 
  ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultPointSymbol(ptSymbol2);
  ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultLineSymbol(lineSymbol2);
  ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultPolygonSymbol(polySymbol2);
  ApplicationOptions.TextAndGraphicsElementsOptions.SetDefaultTextSymbol(textSymbol2);
});

DownloadOptions

Download options control where content being downloaded from online or staged for uploading to online is located. Download options also control the various different locations for unpacking Pro content and for writing ofline map content. Individual download option settings are organized into the same "groupings" of settings as are available on the Pro "Sharing and Download" options UI. In many cases, such "groupings" of settings are mutually exclusive meaning that settingone property "true" in the api will set the other "related" property/properties to false. This is consistent with the operation of the Pro options UI.

For example, take the Unpacking settings for "Other Packages and Files". There are three:

unpack-other-location.png

  • An "Unpack to this location" setting corresponding to ApplicationOptions.DownloadOptions.UnpackOtherLocation.
  • An "Ask where to save before unpacking" setting corresponding to ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation.
  • An "Unpack to the project's home folder" setting corresponding to ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation.

On the UI, toggling any of the three radio buttons for these settings "on" sets the other two to false. Note however, that if the "Unpack to this location" is set false, the path value is not cleared. The ApplicationOptions.DownloadOptions exhibits the same behavior. Setting either of AskForUnpackOtherLocation or UnpackOtherToProjectLocation to true does not clear the value from UnpackOtherLocation in the api but it will be ignored/overridden. To set the application download options to "Unpack to this location", either explicitly set a path value in UnpackOtherLocation or set both AskForUnpackOtherLocation and UnpackOtherToProjectLocation to false.

  //UnpackOther settings control unpacking of anything _other than_
  //a ppkx or aptx. Options are mutually exclusive.

  //Set ApplicationOptions.DownloadOptions.UnpackOtherLocation explicitly to
  //toggle ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation and
  //ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation to false
  //Note: default is typically <My Documents>\ArcGIS\Packages _not_ null.
  //This is the same behavior as on the Pro UI.
  ApplicationOptions.DownloadOptions.UnpackOtherLocation = @"D:\data\for_other";

  //or...to use a location already stored in UnpackOtherLocation as the
  //default without changing it, 
  //set ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation and
  //ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation to false
  //explicitly. This is equivalent to toggling "on" the 'Unpack to this location' setting.
  if (!string.IsNullOrEmpty(ApplicationOptions.DownloadOptions.UnpackOtherLocation)) {
    ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation = false;
    ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation = false;
  }

  //Setting ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation to
  //true overrides any UnpackOtherLocation value and sets 
  //ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation to false.
  //This is the same behavior as on the Pro UI.
  ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation = true;

  //Setting ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation to
  //true overrides any UnpackOtherLocation value and sets 
  //ApplicationOptions.DownloadOptions.AskForUnpackOtherLocation to false.
  //This is the same behavior as on the Pro UI.
  ApplicationOptions.DownloadOptions.UnpackOtherToProjectLocation = true;

Unpacking and Offline Maps settings work in a similar fashion.

Portal Content

Portal content is any content that is retrieved from a portal or online. In the Pro UI, portal content shows up in the catalog pane on the "Portal" tab (and in a Catalog view with "Portal" selected in the breadcrumb\text box). Portal content, in the Pro API, is organized into 3 types*:

  • Items
  • Folders
  • Groups

Portal content derives from ArcGIS.Desktop.Core.Portal.OnlineItem topic 18241, which, in turn, derives from ArcGIS.Desktop.Core.Item topic 9109, the base class for all content in Pro. Note: the ArcGIS.Desktop.Core.Portal namespace is in both the ArcGIS.Desktop.Core.dll and the ArcGIS.Desktop.Catalog.dll assemblies.

*Use portal.GetSignOnUsername to get portal user information and portal.GetPortalInfoAsync to get the logged on user (or anonymous) view of a portal topic 19170.

Portal Items

Of the 3 types in the API, "Items" are the primary content item and represent over 100 different content types that are currently supported by portal such as web maps, feature services, packages, styles, Microsoft office document files, images, mxds, add-ins, and so on (see supported data types and items and PortalItemType enumeration for the complete list of "item" content types). Items are represented by the ArcGIS.Desktop.Core.Portal.PortalItem class topic 18175. Because PortalItem "is a" Item, it can be passed as the parameter to any of the class factories that consume items (eg MapFactory or LayerFactory) assuming that the provided item represents content of the correct type. Portal items can be retrieved either by a portal.GetUserContentAsync call or via a search using portal.SearchForContentAsync.

  var portal = ArcGISPortalManager.Current.GetActivePortal();
  var owner = portal.GetSignOnUsername();
  //get the user content
  var userContent = await portal.GetUserContentAsync(owner);
  //folders...
  foreach (var pf in userContent.PortalFolders) {
    ...

  //items
  foreach (var pi in userContent.PortalItems) {
    ...

Execute a query to retrieve all web maps visible to the user, within his or her organization:

 //assume signed on user is a member of an organization...
 //get the organization id
 var portalInfo = await portal.GetPortalInfoAsync();
 var orgid = portalInfo.OrganizationId;

 var query = PortalQueryParameters.CreateForItemsOfType(PortalItemType.WebMap);
 query.Limit = 100;
 query.OrganizationId = orgid;
 //query the organization for web maps...
 PortalQueryResultSet<PortalItem> results = await portal.SearchForContentAsync(query);
 
 //create a map (in Pro) from the first item
 var result = results.Results.First();
 QueuedTask.Run(() => {
    if (MapFactory.Instance.CanCreateMapFrom(result))
        MapFactory.Instance.CreateMapFromItem(result);
    ...

Portal items that represent file types such as packages, documents, style files, images, etc. can be downloaded to disk via the portalItem.GetItemDataAsync method. This retrieves the original item that (was uploaded) from portal and streams it to the file name you provide, on disk, as the parameter to GetItemDataAsync. Portal items that have no downloadable content will write out an empty file.

 //assume we query for some content
 var results = await portal.SearchForContentAsync(query);

 var portalItem = results.Results.First();//first item
 var folder = @"E:\Temp\Download\";
 var path = System.IO.Path.Combine(folder, portalItem.Name);

 //download the item
 await portalItem.GetItemDataAsync(path);

Portal Folders

Users can organize their portal or online content into folders. Folders are represented by the ArcGIS.Desktop.Core.Portal.PortalFolder class topic 18157 and can be retrieved via a portal.GetUserContentAsync method call (which can return all of a user's content including their folders and items). The corollary for portal.GetUserContentAsync in the Pro UI is the content retrieved for the "My Content" tab in either of the catalog pane or a catalog view.

QueuedTask.Run(() => {
 //Retrieve all the portal folders for the active user
 var portal = ArcGISPortalManager.Current.GetActivePortal();
 var owner = portal.GetSignOnUsername();
 //get the user content
 var userContent = await portal.GetUserContentAsync(owner);
 //folders...
 foreach (var pf in userContent.PortalFolders) {
    ...

Portal Groups

Users can also participate in groups. Groups are represented by the ArcGIS.Desktop.Core.Portal.PortalGroup class topic 18157 and those groups visible to a particular portal user can be retrieved via a portal.GetGroupsFromUserAsync method call. This corresponds to the content retrieved via the "Groups" tab in the catalog pane or view.

QueuedTask.Run(() => {
 //Retrieve all the groups visible to the current signed on user
 var owner = portal.GetSignOnUsername();
 var groups = await portal.GetGroupsFromUserAsync(owner);
 foreach (var group in groups) {
   ...

External Content

Any content that can be browsed (and can be indexed by Pro for browsing) is considered "external content". External content items derive from ArcGIS.Desktop.Core.Item same as project content items. External content items represent the many different file types or folder types (images, text files, file geodatabase folder, style files, zip archives, etc.) that can exist online or on a physical disk drive whether it can be added to Pro or not. For example, when enumerating the contents of a folder, items are returned for all the content, not just the Pro specific content.

Note: The majority of the external content types derive from Item via "Desktop.Internal" classes (as do project items)*. This is an implementation detail not relevant to the public API. Simply treat them as "Items" in your code because that is what they are. The underlying "Desktop.Internal" classes provide Catalog the functionality it needs to show special icons, customize context menus, provide browsing support (for containers) and that sort of thing.

*Some items are instantiated as "ProjectItem" classes directly by ItemFactory.Instance.Create. These are covered in the next section.

ItemFactory and AddItem

Creating and adding content to a project is a three step process:

  1. Create a content item using ItemFactory.Instance.Create using the path or url to the content (whether on disk or online).

  2. Test the returned content for IProjectItem* to see if it can be added to the project.

  3. If it implements IProjectItem, add the content to the project via Project.Current.AddItem. AddItem returns true if the item was successfully added.

Internally, the process of "adding" ensures the item is stored within the correct container (Maps for Maps, Layouts for Layouts, Toolboxes for Toolboxes, etc.) and is added to the internal project repository. Note: Project.Current.AddItem must be called within a QueuedTask lambda.

This example creates a layer file item and tests it to see if it can be added to the Project. (The test will fail and show the message box - layer files can only be added to maps (with LayerFactory) - not projects)

  //test to see if a layer file can be added
  string path = @"E:\Pro\USA\Us_States.lyrx";
  Item item = ItemFactory.Instance.Create(path, ItemFactory.ItemType.PathItem);
  if (item is IProjectItem)
      QueuedTask.Run(() => Project.Current.AddItem((IProjectItem)item));
   else
      //A layer file can NOT be added to a project. It can only be added to a map
      MessageBox.Show($"{item.Path} cannot be added to a project", "Content");

*If you already know that an item is an IProjectItem (e.g. experience, familiarity, snippet, etc.) skip #2 and simply cast the returned item from ItemFactory Create directly. In this example, we know a folder connection is a IProjectItem:

 //Add a folder connection
 string path = @"E:\Pro";
 //Cast item to IProjectItem directly
 var folderItem = ItemFactory.Instance.Create(path) as IProjectItem;
 //Add it
 QueuedTask.Run(() => Project.Current.AddItem(folderItem));

Calls to ItemFactory.Instance.Create create the project item directly in the following cases:

  • FolderConnectionProjectItem (e.g. folders)
  • GDBProjectItem (e.g. .gdb, .sde files)
  • GeoprocessingProjectItem (e.g. .tbx, .gpkx, .pyt files)
  • LocatorsConnectionProjectItem (e.g. .loc files)
  • ServerConnectionProjectItem (e.g. .ags, .wcs, .wms, .wfs, .wmts files)
  • TaskProjectItem (e.g. task files)

However, these content items still have to be added to the project with AddItem. For example:

//These are equivalent workflows
string folderPath = "@C:\\myDataFolder";

//Get an IProjectItem for the folder
var folderItem = ItemFactory.Instance.Create(path) as IProjectItem;

//Or, get a FolderConnectionProjectItem for the folder
var folderItem = ItemFactory.Instance.Create(path) as FolderConnectionProjectItem;

-- MUST call Project.Current.AddItem if you want to add it! --

ItemFactory and Import

Certain file types are silently imported when they are added to a project such as map packages or 10x formatted files (mxds, sxds, styles, etc.) that require conversion. External content types that support importing via the API implement ArcGIS.Desktop.Core.IProjectMultiItem (refer to topic 15858) and can be passed to Project.Current.ImportItem to "add" the content to the project as well using the standard Project.Current.AddItem call. Generally, all 10x file formats that can be imported to Pro support IProjectMultiItem as well as map packages and server connection files.

Using either of Project.Current.ImportItem or Project.Current.AddItem with a IProjectMultiItem adds the content to the project*. However, in those cases where more than one item may be created in the project, ImportItem returns the enumeration directly whereas AddItem does not. The canonical case being an mxd that contains both a map and a layout will create two items in the project when it is added.

*Internally, AddItem calls ImportItem for any IProjectMultiItems to perform the "add". Use the overloaded option of ImportItem to add the map or layout to the project without opening a default pane to display it (refer to ImportItem topic22150.html)

For example, either of these workflows can be used to import an mxd into Pro:

  string mxd = @"E:\Pro\USA\OceanBasemap.mxd";
  //Either...
  var addItem = ItemFactory.Instance.Create(mxd, ItemFactory.ItemType.PathItem) as IProjectItem;
  await QueuedTask.Run(() => Project.Current.AddItem(addItem));
  
  //Or...
  var importItem = ItemFactory.Instance.Create(mxd, ItemFactory.ItemType.PathItem) as IProjectMultiItem;
  var items = await QueuedTask.Run(() => Project.Current.ImportItem(importItem));
  //and also Project.Current.ImportItem(importItem, false) to import and ~not~ open a pane

  //And, of course, MapFactory.Instance.CreateMapFromItem.... not shown

To check whether an item can use Project.Current.ImportItem, test for the presence of the IProjectMultiItem interface:

  var item  = ItemFactory.Instance.Create(itemPath, ItemFactory.ItemType.PathItem);
  //Can it be imported?
  if (item is IProjectMultiItem) {
     //yes it can
  }
  //Can it be added?
  else if (item is IProjectItem) {
     //yes it can
  }

For importing Layouts, the ImportItem(IProjectMultiItem item, bool openPaneAfterImport, bool useExistingMaps = false) overload provides a third parameter - useExistingMaps. Set this to true to connect any existing maps in the project to the relevant map frame content on the imported layout. By default, importing a layout file copies all of the maps referenced by the layout. However, if the maps already exist in the project, setting useExistingMaps =true only copies maps that don't exist in the project, and reuses those that do (exist on the imported layout). This avoids duplicating maps in the project.

Items and Factories

The following factories provide "create" methods that consume Items:

  • ArcGIS.Desktop.Mapping.MapFactory
  • ArcGIS.Desktop.Mapping.LayerFactory
  • ArcGIS.Desktop.Mapping.StandaloneTableFactory

This is useful if you are adding layer files or feature classes (as standalone tables) to a map or maps to a project and have already retrieved an item to the external content at some earlier point in your workflow (e.g. you retrieved a layer file item from a browse of a folder connection). For example:

  //Use the MapFactory to create a map from an external item (e.g. a mapx file or and mxd)

  Item mapItem = ...   //retrieved earlier from ItemFactory or from browsing a folder...

  return QueuedTask.Run(() => {
        // verify that a map can be created from the item
        if (MapFactory.Instance.CanCreateMapFrom(mapItem)) {
          // creates a new map and adds it to the project.  Also opens the mapview
          MapFactory.Instance.CreateMapFromItem(mapItem);
        }
      });

Of course, you can also use overloads on LayerFactory and StandaloneTableFactory that take the physical path ("Uri") to the content without creating an item first

Refresh Items

To refresh a parent item's child item collection, item.Refresh() can be called. Usually this shouldn't be necessary, however certain items can get out of sync with their child item collection. For example, between the time when new child items may be added on disk external to Pro and the parent folder item updates its child item cache.

When refreshing items, certain items can only be refreshed on the Main CIM Thread, MCT, via QueuedTask.Run whereas others can be refreshed on any thread, to include the UI. To determine if the MCT is required for an item refresh, item.IsMainThreadRequired should be called. If the property is set to true then item.Refresh() must be called within a QueuedTask.Run(). If item.IsMainThreadRequired is set to false, then any thread can be used though BackgroundTask.Run() is preferred over the UI:

  //var contentItem = ...
  //Check if the MCT is required for Refresh()
  if (contentItem.IsMainThreadRequired) {
     //QueuedTask.Run must be used if item.IsMainThreadRequired
     //returns true or a CalledOnWrongThreadException will be thrown.
     QueuedTask.Run(() => contentItem.Refresh());
  }
  else {
     //if item.IsMainThreadRequired returns false, any
     //thread can be used to invoke Refresh(), though
     //BackgroundTask is preferred.
     contentItem.Refresh();

     //Or, via BackgroundTask
     ArcGIS.Core.Threading.Tasks.BackgroundTask.Run(() =>
        contentItem.Refresh(), ArcGIS.Core.Threading.Tasks.BackgroundProgressor.None);
  }

Getting Item Content

GetItems

Content can either be accessed via a GetItems<T> call on the parent item (for example a folder or geodatabase) or on the project instance (or via portal as portal items, ref. portal content). If an item has children that can be enumerated with GetItems then its IsContainer property will be true. Calling GetItems on the project instance enumerates all of the project items - that is, all of the content that has been added to the project at that point in time - whereas calling GetItems on a folder or geodatabase item, for example, enumerates all of the external items that they each contain (regardless of whether the folder or geodatabase have been added to the project or not). GetItems on a "container" (like a folder or geodatabase) always searches one level deep. To perform a multi-level search (e.g. to drill down through a folder hierarchy), the returned Items should be tested for "IsContainer" equals true and their content likewise enumerated with GetItems (recursively) and so on.

As GetItems<T> is a generic function, it can be constrained to return only items of a particular type using its template parameter "T". This is especially useful when searching the content of the project for just items of a particular type. For example:

  //get just the maps - use MapProjectItem
  var maps = Project.Current.Getitems<MapProjectItem>();

  //Get a specific map
  var map = Project.Current.GetItems<MapProjectItem>().FirstOrDefault(
                                               m => m.Name == "Map 1");

  //get just the database connections - use GDBProjectItem
  var gdbs = Project.Current.Getitems<GDBProjectItem>();

  //get just the layouts - use LayoutProjectItem
  var layouts = Project.Current.Getitems<LayoutProjectItem>();

  //etc.

  //Just get them all - no constraints
  var allItems = Project.Current.GetItems<Item>();

As GetItems<T> returns an IEnumerable, the result can be extended with Linq expressions to further refine and filter the retrieved content. For example, this is a search on the first folder connection of a given name in the project for any mxd files it contains:

    //Declarations in a view model, for example
    private static readonly object _lock = new object();
    private ObservableCollection<Item> _mxdResults = = new ObservableCollection<Item>();

    ...
    //Call to BindingOperations to sync access to the ObservableCollection, eg in the ctor
    BindingOperations.EnableCollectionSynchronization(_mxdResults , _lock);
    ...

    ///<summary>Property for binding of search results</summary>
    public ObservableCollection<Item> MxdResults => _mxdResults ;

    /// <summary>Performs the search for mxds on the given folder</summary>
    private async Task SearchFirstFolderForMxdsAsync(string Folder) {
       // find the folder project item - Use Ling FirstOrDefault extension on the returned IEnumerable
       var folder = Project.Current.GetItems<FolderConnectionProjectItem>().FirstOrDefault(f => f.Path == Folder);
       await QueuedTask.Run(() => {
          lock(_lock) {
             _mxdResults.Clear();
             //Call GetItems to get the folder content (one level deep)
             foreach (var item in folder.GetItems()) {
                 if (item.Name.EndsWith("mxd"))
                    _mxdResults.Add(item);
             }
        }
     }

Content returned from GetItems are snapshot collections. They are not refreshed if the child content of given parent project item changes (e.g. a new file or feature class is added). Content must be re-queried.

Project also provides a public Task<IEnumerable<Item>> SearchAsync(string query) function that supports a keyword search of project item content based on the information that a given project item may contain in its metadata (e.g. in its keywords, tags, summary, etc.). Refer to topic 9205 in the API Reference.

FindItems

FindItems is useful when you already have a path or catalog path to an item on disk or in a geodatabase. FindItems first searches for the content in the Pro index (which includes content that has been added to the project). If the item is found in the index, the item is returned. If the item is not found in the index, FindItems checks the path to assure the content is valid, and, if so, creates a new item. Additionally, if the path is either to a folder or a file geodatabase, then the item is added to the project and will be indexed. If the path is bad, null is returned.

 //retrieve a pdf
 var pdfPath = @"E:\Temp\Layout1.pdf";
 var pdf = Project.Current.FindItem(pdfPath);

 //retrieve a folder (the folder will be added to the project)
 var folderPath = @"E:\Data\SampleData";
 var folder = Project.Current.FindItem(folderPath);

 //retrieve a feature class and select it in the Catalog pane (assuming the catalog pane has focus)
 var fcPath = @"E:\Pro\CommunitySampleData\Interacting with Maps.gdb\Crimes";
 var fc = Project.Current.FindItem(fcPath);

If the item(s) is a project content item, it can be further cast to the relevant "ProjectItem" class (i.e. "project items" like map, style, layout, scene, toolbox, etc.)

Geodatabase Content

Assuming you have retrieved an item corresponding to geodatabase content, the next step is usually to access the underlying ArcGIS.Core.Data dataset it contains. Items corresponding to geodatabase content are usually retrieved from the browse dialog when a user browses into a geodatabase and selects one of the geodatabase objects within it or from an item selected in the Catalog window or pane. There are many types of datasets, some examples are: a feature dataset, a feature class, a table, or a relationship class. The dataset types currently supported are defined by the ArcGIS.Core.Data enumeration DatasetType

A number of methods have been added to the ItemFactory class to facilitate retrieval of dataset and data definition. These are GetDatasetType, GetDataset and GetDefinition. There are also the corresponding CanGetDataset and CanGetDefinition properties.

First, lets examine how to retrieve the geodatabase content from an item chosen in the Browse dialog.

  var openDlg = new OpenItemDialog
  {
    Title = "Select a Feature Class",
    InitialLocation = @"C:\Data",
    MultiSelect = false,
    BrowseFilter = BrowseProjectFilter.GetFilter(ArcGIS.Desktop.Catalog.ItemFilters.geodatabaseItems_all)
  };

  //show the browse dialog
  bool? ok = openDlg.ShowDialog();
  if (!ok.HasValue || openDlg.Items.Count() == 0)
    return;   //nothing selected

  await QueuedTask.Run(() =>
  {
    // get the item
    var item = openDlg.Items.First();

    // see if the item has a dataset
    if (ItemFactory.Instance.CanGetDataset(item))
    {
      // get it
      using (var ds = ItemFactory.Instance.GetDataset(item))
      {
        // access some properties
        var name = ds.GetName();
        var path = ds.GetPath();

        // if it's a featureclass
        if (ds is ArcGIS.Core.Data.FeatureClass fc)
        {
          // create a layer 
          var layer = LayerFactory.Instance.CreateLayer(new Uri(path), MapView.Active.Map);
 
          // continue
        }
      }
    }
  });

Next, lets retrieve the geodatabase content from a selection in the Catalog pane. In order to know when an item is selected in the Catalog pane, subscribe to the ProjectWindowSelectedItemsChangedEvent.

  // subscribe to event
  ProjectWindowSelectedItemsChangedEvent.Subscribe(async (ProjectWindowSelectedItemsChangedEventArgs args) =>
  {
    if (args.IProjectWindow.SelectionCount > 0)
    {
      // get the first selected item
      var selectedItem = args.IProjectWindow.SelectedItems.First();

      await QueuedTask.Run(() =>
      {
        // get the datasetType
        var dataType = ItemFactory.Instance.GetDatasetType(selectedItem);

        // get the dataset Defintion
        if (ItemFactory.Instance.CanGetDefinition(selectedItem))
        {
          using (var def = ItemFactory.Instance.GetDefinition(selectedItem))
          {
            if (def is ArcGIS.Core.Data.FeatureClassDefinition fcDef)
            {
              var oidField = fcDef.GetObjectIDField();
              var shapeField = fcDef.GetShapeField();
              var shapeType = fcDef.GetShapeType();
            }
            else if (def is ArcGIS.Core.Data.Parcels.ParcelFabricDefinition pfDef)
            {
              string ver = pfDef.GetSchemaVersion();
              bool enabled = pfDef.GetTopologyEnabled();
            }

            // etc
          }
        }

        // get the dataset
        if (ItemFactory.Instance.CanGetDataset(selectedItem))
        {
          using (var ds = ItemFactory.Instance.GetDataset(selectedItem))
          {
            if (ds is ArcGIS.Core.Data.FeatureDataset fds)
            {
              // open featureclasses within the feature dataset
              // var fcPoint = fds.OpenDataset<FeatureClass>("Point");
              // var fcPolyline = fds.OpenDataset<FeatureClass>("Polyline");
            }
            else if (ds is FeatureClass fc)
            {
              var name = fc.GetName() + "_copy";

              // create
              LayerFactory.Instance.CreateLayer(new Uri(fc.Path), MapView.Active.Map);
            }
            else if (ds is Table table)
            {
              var name = table.GetName() + "_copy";

              var stParams = new StandaloneTableCreationParams(table);
              stParams.Name = name;

              // create
              StandaloneTableFactory.Instance.CreateStandaloneTable(stParams, MapView.Active.Map);
            }
          }
        }
      });
    }
  });

Note: If you are using ArcGIS Pro 2.8 or previous, then accessing the underlying dataset from the item requires a different pattern

  1. Access the path from the Item.Path property.
  2. Parse out the geodatabase connection path from the item path (this can be a path to a file gdb, sde file, or even folder).
  3. Instantiate the datasource pointed to by the connection path.
  4. Retrieve the dataset named in the Item.Name property from the datasource.

If you are starting from a path (and not an item) it is actually more straightforward to create an Item from the path first and then follow the 4 step procedure above rather than parsing the path for both the datasource and dataset name directly (and attempting to skip creating an Item).

This example illustrates retrieving a feature class from an item returned from the browse dialog:

 //using ArcGIS.Desktop.Catalog;
 //using ArcGIS.Desktop.Core;
 //using ArcGIS.Desktop.Framework.Threading.Tasks;
 //using ArcGIS.Core.Data;

 var openFc = new OpenItemDialog {
   Title = "Select a Feature Class",
   InitialLocation = @"C:\Data\GDB_data",
   MultiSelect = false,
   //filter for feature classes
   BrowseFilter = BrowseProjectFilter.GetFilter(ArcGIS.Desktop.Catalog.ItemFilters.featureClasses_all)
 };

 //show the browse dialog
 bool? ok = openFc.ShowDialog();			
 if (!ok.HasValue || openFc.Items.Count() == 0)
    return;//nothing selected

 //retrieve the selected feature class
 QueuedTask.Run(()=> {
   var fc_item = openFc.Items.First();
   //see below for the GDBItemHelper class
   using (var fc = GDBItemHelper.GetDatasetFromItem(fc_item) as FeatureClass)) {
      //do something with the fc
      using (var rows = fc.Search()) {
	 //etc
      }
   }
   ...

This snippet shows the implementation of a GDBItemHelper class that can be used for extracting the path from a File GDB, Enterprise GDB, or Shapefile GDB item and instantiating its contained dataset:

 //using ArcGIS.Core.Data;
 //
 //GDBItemHelper utility class for retrieving datasets from File, Enterprise, or Shapefile GDB items
 internal static class GDBItemHelper {
 
   // Keywords
   public const string FeatureClassKeyword = "Feature Class";
   public const string FeatureDatasetKeyword = "Feature Dataset";
   public const string TableKeyword = "Table";
   public const string RelationshipClassKeyword = "Relationship Class";

    //Must use QueuedTask
   public static Dataset GetDatasetFromItem(Item item) {
      //self-documenting...
      if (!QueuedTask.OnWorker)
         throw new ArcGIS.Core.CalledOnWrongThreadException();

      //File
      if (item.TypeID.StartsWith("fgdb_") || item.TypeID.EndsWith("_fgdb"))
         return OpenDatasetForExtension(".gdb", item);
      //Enterprise
      if (item.TypeID.StartsWith("egdb_") || item.TypeID.EndsWith("_egdb"))
         return OpenDatasetForExtension(".sde", item);
      //Shape
      if (item.TypeID.StartsWith("shapefile_"))
         return OpenDatasetForExtension(".shp", item);
      return null;
   }

   //use this flavor if you only have a path
   public static Dataset GetDatasetFromPath(string path) {
       //self-documenting...
       if (!QueuedTask.OnWorker)
          throw new ArcGIS.Core.CalledOnWrongThreadException();

       //check the path is valid - File, Enterprise, Shape
       int indexOfgdb = path.LastIndexOf(".gdb", StringComparison.InvariantCultureIgnoreCase);
       if (indexOfgdb == -1)
          indexOfgdb = path.LastIndexOf(".sde", StringComparison.InvariantCultureIgnoreCase);
       if (indexOfgdb == -1)
          indexOfgdb = path.LastIndexOf(".shp", StringComparison.InvariantCultureIgnoreCase);
       if (indexOfgdb == -1)
          return null;

       //make the item
       var item = ItemFactory.Instance.Create(path);
       //retrieve the dataset
       return GetDatasetFromItem(item);
   }

   private static Dataset OpenDatasetForExtension(string extension, Item item) {

       int indexOfgdb = item.Path.LastIndexOf(extension,
                                    StringComparison.InvariantCultureIgnoreCase);
       if (indexOfgdb == -1)
          return null;

       string gdbPath = item.Path.Substring(0, indexOfgdb + extension.Length);

       if (extension == ".sde") {
          using (var geodatabase = new Geodatabase(
                                      new DatabaseConnectionFile(new Uri(gdbPath)))) {
            if (item.TypeKeywords.Contains(FeatureClassKeyword))
              return geodatabase.OpenDataset<FeatureClass>(item.Name);

            if (item.TypeKeywords.Contains(FeatureDatasetKeyword))
              return geodatabase.OpenDataset<FeatureDataset>(item.Name);

            if (item.TypeKeywords.Contains(TableKeyword))
              return geodatabase.OpenDataset<Table>(item.Name);

            if (item.TypeKeywords.Contains(RelationshipClassKeyword))
              return geodatabase.OpenDataset<RelationshipClass>(item.Name);
         }
      }
      else if (extension == ".gdb") {
         using (var geodatabase = new Geodatabase(
                                     new FileGeodatabaseConnectionPath(new Uri(gdbPath)))) {
            if (item.TypeKeywords.Contains(FeatureClassKeyword))
              return geodatabase.OpenDataset<FeatureClass>(item.Name);

            if (item.TypeKeywords.Contains(FeatureDatasetKeyword))
              return geodatabase.OpenDataset<FeatureDataset>(item.Name);

            if (item.TypeKeywords.Contains(TableKeyword))
              return geodatabase.OpenDataset<Table>(item.Name);

            if (item.TypeKeywords.Contains(RelationshipClassKeyword))
              return geodatabase.OpenDataset<RelationshipClass>(item.Name);
         }
      }
      else if (extension == ".shp") {
         var shapePath = System.IO.Path.GetDirectoryName(gdbPath);
         using (var shape = new FileSystemDatastore(new FileSystemConnectionPath(
							new Uri(shapePath), 
                                                        FileSystemDatastoreType.Shapefile))) {
           return shape.OpenDataset<FeatureClass>(item.Name);
         }
     }
     return null;
   }
 }

Item Metadata

The Item class includes these two properties which provide access to the item’s metadata as an XML document in the ArcGIS metadata format.

An item’s metadata lets you document the item’s content. For example, you can describe where the item’s data came from, how recent it is, what processes have been run on it, what information is available in the fields of its attribute table, and how the item should be used including any legal disclaimers regarding that use. Learn more about viewing and editing metadata in ArcGIS Pro: view and edit metadata.

The item’s metadata is returned as a string representing the ArcGIS metadata XML document. Use that string to create an XML document that can be manipulated using the XML parser of your choice. For example, you can transform the provided XML to an HTML page that can be displayed outside of ArcGIS Pro. Or, you can use the XML parser’s methods to programmatically update the metadata content and save the changes back to the original item using the SetXml method. The metadata saved back to the item must be in the ArcGIS metadata format.

The ArcGIS metadata format is documented in the ArcGIS Metadata Toolkit, which can be downloaded from the Esri Support site. The toolkit contains an XML DTD that defines the structure of an ArcGIS metadata XML document. It also contains an Excel spreadsheet that documents the elements, and indicates if they are populated automatically by ArcGIS, which metadata styles they participate in, where they appear in the metadata editor, and so on. Sample XML documents are also provided to help correlate the ArcGIS metadata format with standard XML formats such as the FGDC CSDGM and ISO 19115/19139 standards.

Catalog Context

The context (usually "what is selected") of either the Catalog Pane or a Catalog View can be accessed via their ArcGIS.Desktop.Core.IProjectWindow interface (see topic 14659).

To retrieve the catalog pane or any catalog view use:

  • Project.GetActiveCatalogWindow() if you want the active catalog view. If no catalog view is active, this returns null.
  • Project.GetCatalogPane() if you want the catalog pane (whether it is active or not). Specify createIfNecessary=false if you do not the catalog pane created if it has not been created previously.
  • FrameworkApplication.ActiveWindow and test it for IProjectWindow. If the ActiveWindow is an IProjectWindow it can be either of the catalog pane or a catalog view otherwise the IProjectWindow will be null.
  • FrameworkApplication.Panes enumeration for a specific catalog view (active or not).
 //Get the active catalog window - either the pane ~or~ a catalog view
 var window = FrameworkApplication.ActiveWindow as ArcGIS.Desktop.Core.IProjectWindow
 if(window != null) {
  ...

 //Get the Catalog pane (whether it is the active window or not)
 var catalog = Project.GetCatalogPane();//will create the pane if needed
 ...

 //enumerate the panes collection
 foreach (var pane in FrameworkApplication.Panes) {
 ...

Getting Selected Items

Accessing an item selection requires the IProjectWindow (catalog pane or view) whose selection you want access and enumerating its IEnumerable<Item> SelectedItems { get; } property. Each catalog window maintains its own independent selection (view or the pane). Subscribe to the ProjectWindowSelectedItemsChangedEvent event to be notified of selection changes.

//Get the active catalog window - either the pane ~or~ a catalog view
var window = FrameworkApplication.ActiveWindow as ArcGIS.Desktop.Core.IProjectWindow
var item = window?.SelectedItems.First();
//do something with the selection...

//Get the Catalog pane (whether it is the active window or not)
var catalog = Project.GetCatalogPane();//will create the pane if needed
//get whatever is selected on it
var items = catalog.SelectedItems;

Selecting Items

Selection is performed on project items on the project tab of the catalog pane or catalog views. To perform a selection of items on a catalog window, use its IProjectWindow.SelectItemAsync method (topic19182.html). In no particular order:

  • Retrieve the item to be selected
  • Get the IProjectWindow (catalog pane or view) you want to select the item on
  • [Optional] Get the container you want the item selected in (some items can live in more than one container)

Retrieve the item using any of the methods available for searching or browsing for items such as GetItems. The IProjectWindow should be acquired as described in the Catalog Context above. The last parameter to SelectItemAsync is the container within which you want the specified item to be selected. Catalog content can, in some cases, be browsed either via its "primary" container or, because it may represented as a physical file or folder, via the Folders container (assuming that its parent folder has been added to the project). The same is true of geodatabase content, such as a feature class, which can be browsed via its parent geodatabase item within the "Databases" container or, possibly, the "Folders" container.

When the SelectItemAsync container parameter is set to null, the first occurrence of the item found in any container is the one that is selected. If Multiple containers can hold a particular item, they will be searched in the same order as their visual order/arrangement within the catalog window (pane or view) you are selecting it in. For example, selecting a map or toolbox:

  //"projectWindow" can be either the catalog pane or a catalog view...
  //Maps only occur in the "Maps" container so no container need be specified
  string mapName = "Map";
  var map = Project.Current.GetItems<MapProjectItem>().FirstOrDefault(m => m.Name == mapName);
  projectWindow.SelectItemAsync(map, true, null);//optionally "await"

  //Select the toolbox in the Toolboxes container. Because it is searched
  //before "Folders", and null is specified, the toolbox within the Toolboxes container 
  //will be selected.
  string gpName = "Interacting with Maps.tbx";
  var toolbox = Project.Current.GetItems<GeoprocessingProjectItem>().FirstOrDefault(tbx => tbx.Name == gpName);
  projectWindow.SelectItemAsync(toolbox, true, null);//optionally "await"

Specifying a Container

For those cases where the container within which you want a particular item selected cannot be inferred, the container must be specified. To access the container for use in selecting items, use Project.Current.ProjectItemContainers to enumerate through the available containers*.

Containers have a localizable display name (their "Name" attribute) which is shown in the catalog windows as well as a non-localizable "type" or "Path" attribute. The "Path" attribute is the preferred attribute to use when identifying a specific container. It correlates with the value for the "key" parameter for Project.Current.GetProjectItemContainer(string key) as well. The complete set of containers in catalog is listed below along with their display name and path.

Note: Project.Current.ProjectItemContainers, like many collections in Pro, is a snapshot selection. What that means is, depending on the actual content of a given project at any given point in time, the list of instantiated project containers can vary. In other words, depending on your project content, some containers can be null. (Databases, Folders, Layouts, Locators, Maps, Styles, and Toolboxes are always present). There are also non-visible internal project containers within the collection of containers which can be ignored.

* Project containers are all registered in the esri_core_projectContainers category in the ADGeoDatabase.daml.

Public Containers

Display Name (English) Path or "Key" (Non-localizable) Item content
Databases GDB Database content
Folders FolderConnection Files and Folders
Layouts Layout Layouts
Locators LocatorsConnection Locators
Maps Map Maps
Raster Function Templates RasterFunctionTemplates Raster function templates
Reviewer Batch Jobs DataReviewerBatchJobs Reviewer batch jobs
Reviewer Results DataReviewerResources Reviewer results
Servers ServerConnection Server connections
Styles Style Styles
Tasks Task Tasks
Toolboxes GP Toolboxes
Workflows WorkflowConnection Workflows

Use the path attribute to select a given container from the collection or the key value with GetProjectItemContainer - depending on your project content some containers can be null:

 //Selecting the maps container
 var maps = Project.Current.ProjectItemContainers.First(c => c.Path == "Maps");
 //or with key
 maps = Project.Current.GetProjectItemContainer("Map");

 //Selecting the Folders container
 var folders = Project.Current.ProjectItemContainers.First(c => c.Path == "FolderConnection");
 folders = Project.Current.GetProjectItemContainer("FolderConnection");

//Selecting the Databases container
 var databases = Project.Current.ProjectItemContainers.First(c => c.Path == "GDB");
 databases = Project.Current.GetProjectItemContainer("GDB");

Assuming the desired item has been retrieved along with the relevant catalog window and container, perform the selection: Select the given item in the container using its SelectItemAsync method.

  //retrieve a feature class and select it in the Catalog pane (assuming the catalog pane has focus)
  var fcPath = @"E:\Pro\CommunitySampleData\Interacting with Maps.gdb\Crimes";
  var fc = Project.Current.FindItem(fcPath);

  //Get the Folders container
  var folderContainer = Project.Current.GetProjectItemContainer("FolderConnection");

  //Select the fc
  var projectWindow = Project.GetCatalogPane();
  await projectWindow.SelectItemAsync(fc, true, folderContainer);

 //Get a toolbox and select it in the Folder container, assuming its parent
 //folder has been added to the list of Folders
 string gpName = "Interacting with Maps.tbx";
 var toolbox = Project.Current.GetItems<GeoprocessingProjectItem>().FirstOrDefault(tbx => tbx.Name == gpName);
 var folderContainer = Project.Current.ProjectItemContainers.First(c => c.Path == "FolderConnection");
 
 var projectWindow = Project.GetCatalogPane();
 await projectWindow.SelectItemAsync(toolbox, true, folderContainer);
 

ProjectWindowSelectedItemsChangedEvent

Subscribe to ArcGIS.Desktop.Core.Events.ProjectWindowSelectedItemsChangedEvent to be notified of selection changes on any catalog window (pane or views). The ProjectWindowSelectedItemsChangedEventArgs identifies the IProjectWindow and its DAML Id, that triggered the selection changed event. The IProjectWindow con the event args an be accessed to retrieve what is selected.

//register for selection changed (eg in a dockpane)
internal class LayoutEventSpyViewModel : DockPane {

    protected override Task InitializeAsync() {
ArcGIS.Desktop.Core.Events.ProjectWindowSelectedItemsChangedEvent.Subscribe((args) => {
        var names = string.Join(",", args.IProjectWindow.SelectedItems.Select(i => i.Name).ToList());
        //etc
      });
    ...

Detecting Project Changes

Addins needing to detect, or listen for, changes to the project should subscribe to the ArcGIS.Desktop.Core.Events.ProjectItemsChangedEvent event. ProjectItemsChangedEvent is fired whenever the collection of project items associated with the project is changed. Addins should examine the ProjectItemsChangedEventArgs.Action property to determine the nature of the change (add, remove, replace, etc.) and execute whatever is their associated logic accordingly. In this example, the addin code is listening for NotifyCollectionChangedAction.Add associated with new maps and layouts:

 ...

 ArcGIS.Desktop.Core.Events.ProjectItemsChangedEvent.Subscribe(args => {
   //Collect the items involved in the event
   var items = new List<Item>();

   if (args.ProjectItem is Item item)//just one item
      items.Add(item);
   else if (args.ProjectItemsCollection != null)//more than one
      items.AddRange(args.ProjectItemsCollection);

   switch (args.Action) {
      case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
         var layouts = items.OfType<LayoutProjectItem>()?.ToList() ??
		 new List<LayoutProjectItem>();
         //TODO process new layout(s)
         var maps = items.OfType<MapProjectItem>()?.ToList() ??
		 new List<MapProjectItem>();
         //TODO process new map(s)
         break;
      case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
         //TODO handle remove
         break;
      case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
         //TODO handle replace
         break;
      default:
         break;
   }
 });

Favorites

Favorites is a collection of frequently used connections to folders, databases, toolboxes, servers, custom styles and statistical data collections. The FavoritesManager class allows developers to programmatically customize the favorites collection which are accessible in the UI via the Catalog pane or Catalog view. Any changes made to favorites are saved instantly when the change is made and is independent of project saving.

The following example shows how to add a folder to the favorites collection. First create an item using the ItemFactory class. Use the GetFavorite method to verify the item is not already in the collection. This function returns a non-null Favorite object if the item can be found in the favorites collection. If the favorite doesn't exist, the CanAddAsFavorite function should be used before adding the item to ensure that the item type is supported by the favorites collection.

  var itemFolder = ItemFactory.Instance.Create(@"d:\data");

  // is the folder item already a favorite?
  Favorite fav = FavoritesManager.Current.GetFavorite(itemFolder);
  if (fav == null)
  {
    if (FavoritesManager.Current.CanAddAsFavorite(itemFolder))
    {
      fav = FavoritesManager.Current.AddFavorite(itemFolder);
    }
  }

Just as within the ArcGIS Pro application, if your organization has an enterprise geodatabase, a GIS server, or a folder containing data used by all projects, you can add these items to your favorites and set them to be added automatically to all new projects you create. This behavior is controlled by the addToAllNewProjects parameter in the AddFavorite or InsertFavorite methods or by the IsAddedToAllNewProjects property on the Favorite class.

Here's an example of adding a new favorite with the flag set.

  var itemFolder = ItemFactory.Instance.Create(@"d:\data");

  // is the folder item already a favorite?
  Favorite fav = FavoritesManager.Current.GetFavorite(itemFolder);
  if (fav == null)
  {
    if (FavoritesManager.Current.CanAddAsFavorite(itemFolder))
    {
      fav = FavoritesManager.Current.AddFavorite(itemFolder, true);
    }
  }

And here's a code example showing the flag being toggled for an existing folder connection.

  var itemFolder = ItemFactory.Instance.Create(@"d:\data");

  // is the folder item already a favorite?
  Favorite fav = FavoritesManager.Current.GetFavorite(itemFolder);
  if (fav != null)
  {
    // if it's already set to be added to all new projects
    if (fav.IsAddedToAllNewProjects)
      // clear the flag
      FavoritesManager.Current.ClearIsAddedToAllNewProjects(fav.Item);
    else
      // set the flag
      FavoritesManager.Current.SetIsAddedToAllNewProjects(fav.Item);
  }

To retrieve the current set of favorites use the GetFavorites method. This returns a readonly list of Favorite items. You can iterate through the favorites, examining each item one by one. The Item property on the Favorite class gives access to the underlying item object. Here's some sample code illustrating this.

var favorites = FavoritesManager.Current.GetFavorites();
foreach (var favorite in favorites)
{
  bool isAddedToAllProjects = favorite.IsAddedToAllNewProjects;
  // retrieve the underlying item of the favorite
  Item item = favorite.Item;

  // Item properties
  var itemType = item.TypeID;
  var path = item.Path;

  // if it's a folder item
  if (item is FolderConnectionProjectItem)
  {
  }
  // if it's a goedatabase item
  else if (item is GDBProjectItem)
  {
  }
  // else 
}

Finally the FavoritesChangedEvent is fired every time the favorites collection is changed. Addins interested in changes to the favorites collection should subscribe to this event.

 ArcGIS.Desktop.Core.Events.FavoritesChangedEvent.Subscribe((args) =>
 {
   // favorites have changed
   int count = FavoritesManager.Current.GetFavorites().Count;
 });

Browse Dialog Filters

ArcGIS Pro allows developers to programmatically create and configure browse dialog filters for use with the OpenItemDialog and SaveItemDialog to control what is visible and can be browsed.

To create browse dialog filters, developers can either create an instance of the BrowseProjectFilter class configuring it with the item types they wish to filter for, or can use one of the many predefined filters, or can do both. In Pro, filters are identified by DAML ids which are registered in the "esri_browseFilters" category in the respective DAML files for each Pro extension.

To retrieve a Pro filter at runtime, use the BrowseProjectFilter.GetFilter(filterName) static method where filterName is the DAML id of the filter to be retrieved. Pro also provides an ArcGIS.Desktop.Catalog.ItemFilters class which has a number of properties defined representing the DAML ids for filters for many of the most common ArcGIS Pro content types. Refer to ProSnippets: Browse Dialog Filters for usage examples of every ArcGIS Pro filter. By default, a new (unconfigured) BrowseProjectFilter will browse all file types supported by Pro.

 //Retrieve the pre-defined filter for all file types 
 var fileFilter = BrowseProjectFilter.GetFilter("esri_browseDialogFilters_browseFiles");
 fileFilter.FileExtension = "*.*";//restrict to specific extensions as needed
 fileFilter.BrowsingFilesMode = true;
 //Specify a name to show in the filter dropdown combo box - otherwise the name
 //will show as "Default"
 fileFilter.Name = "All Files";

 //This is equivalent to configuring a filter to retrieve all supported file types
 var defaultFilter = new BrowseProjectFilter();
 //Specify a name to show in the filter dropdown combo box - otherwise the name
 //will show as "Default"
 defaultFilter.Name = "All Pro Files";

 //Retrieve the pre-defined filter for adding content to a map using ItemFilters
 //Note: this is an example of a composite filter which is covered in the next
 //section
 var contentFilter = BrowseProjectFilter.GetFilter(ItemFilters.composite_addToMap);
 
 //Use the filter with the OpenItemDialog
 var dlg = new OpenItemDialog() {
    BrowseFilter = fileFilter, //or use defaultFilter or contentFilter...
    Title = "Browse Content"
 };
 //show the dialog and retrieve the selection if there was one
 if (!dlg.ShowDialog().Value)
   return;
 var item = dlg.Items.First();

Composite Filters

Filters can be combined with other filters to build your own composites. Composite filters show up as multiple entries in the Open and Save ItemDialogs filter combos - with each entry representing an individual filter contained within the overall composite. For example, the screenshot below shows the names of the individual filters comprising the esri_browseDialogFilters_composite_addToMap filter - identified by the ItemFilters.composite_addToMap member (and used in the example above):

OpenItemDialogCombo

To create a composite filter, add each individual filter to be used in the composite to the BrowseProjectFilter's Filters collection via its AddFilter method. In the following example a composite filter is created to browse for both .lyr and .lyrx files:

  var compositeFilter = new BrowseProjectFilter();//this will be our composite
  //Add the Pro filter for .lyr files
  compositeFilter.AddFilter(BrowseProjectFilter.GetFilter(
                                          "esri_browseDialogFilters_layers_lyr"));
  //Add the Pro filter for .lyr _X_ files
  compositeFilter.AddFilter(BrowseProjectFilter.GetFilter(
                                          "esri_browseDialogFilters_layers_lyrx"));

  //Display the composite filter in an Open Item dialog
  var dlg = new OpenItemDialog {
    Title = "Open LYR and LYRX files",
    InitialLocation = @"E:\Pro",
    BrowseFilter = compositeFilter
  };
  bool? ok = dlg.ShowDialog();

Notice that the names of both the filters show up in the browse dialog:

compositeFilter

Customizing Filters By Type ID

At its most granular level, filters work by filtering in (or out) items based on their underlying type id. Type ids are string identifiers assigned to each, individual, item type in Pro to uniquely identify its given content type. Content types include file types such as mxds, layer files, package types and so forth as well as geodatabase types and online types as well. Content type ids are declared in the Pro extension daml files within the esri_item category. The complete Pro type id reference can be found here: ArcGIS Pro TypeID Reference. The corresponding type id for a given item is accessed via its Item.TypeID property.

Content types can be added or removed to an existing filter via the BrowseProjectFilter AddCanBeTypeId and AddMustNotBeTypeId methods. Using our previous example, instead of making a composite filter to show .lyr and .lyrx files, we will instead modify the esri_browseDialogFilters_layers_lyrx filter to also include .lyr files. The example uses BrowseProjectFilter.AddCanBeTypeId("layer_general") to add the type id "layer_general" to the filter. layer_general is the corresponding type id for .lyr files:

  var layerFileFilter = BrowseProjectFilter.GetFilter(
                          "esri_browseDialogFilters_layers_lyrx"));
  //Add lyr files to this browse filter by type id
  layerFileFilter.AddCanBeTypeId("layer_general");
  //Provide a display name for the filter (optional)
  layerFileFilter.Name = "Layer Files (LYRX) and Layer Files (LYR)";
 
  //TODO: Show the OpenItemDialog as before...

Notice this is a single filter for both .lyr and .lyrx files and not a composite as before:

BrowseFilterNamedDAMLParameter

Type ids can also be used in-lieu of predefined filters to create your own custom browse experience from scratch. In the following example, the filter is configured to browse only for polygon features. Notice the use of BrowseProjectFilter.AddCanBeTypeId("fgdb_fc_polygon") to specify just polygon feature classes as the filtered type to be shown in the dialog:

  //In this case we do not use a pre-defined filter
  var polyFilter = new BrowseProjectFilter();//this will be our polygon-only filter
  polyFilter.AddCanBeTypeId("fgdb_fc_polygon");//filter only polygons
  polyFilter.Name = "Polygons only";

  //Display the filter in an Open Item dialog
  var dlg = new OpenItemDialog {
    Title = "Open Polygon Feature Classes",
    InitialLocation = @"E:\Pro",
    BrowseFilter = polyFilter
  };
  bool? ok = dlg.ShowDialog();

BrowseFilterPolygonsOnly

You can further configure the browsing behavior of the filter by enabling or disabling browsing into specific container types (such as file geodatabases or folders) using AddDoBrowseIntoTypeId and AddDontBrowseIntoTypeId respectively.

Customizing Filters By Flags

In conjunction with type ids, filters also allow similar customizations via predefined BrowseProjectFilter.FilterFlags. The complete set of available filter flags are found here: BrowseProjectFilter.FilterFlag enumeration. They work in exactly the same way as type ids except that the flags operate at a higher-level of granularity (than type ids). An individual flag typically represents the consolidation of multiple type ids where the name of the flag gives an indication of the respective content types it filters. For example the flags BrowseProjectFilter.FilterFlag.FeatureClass, BrowseProjectFilter.FilterFlag.Layer, and BrowseProjectFilter.FilterFlag.AddToMap represent feature class, layer file, and "all types suitable for adding to a map" content types respectively. In many cases they are synonymous with equivalent (predefined) filters declared in ItemFilters.

Use flags in the same way as you use type ids. Add or Exclude content types via the AddCanBeFlag and AddMustNotBeFlag methods and configure the browse behavior of containers with flags via AddDontBrowseIntoFlag.

Use of flags is not mutually exclusive from the use of type ids. In fact, Flags can be combined with type ids to provide both a coarse and very fine grained level of filter customization. For example a flag can be used to "add" a large number of content types to a filter which can then be refined by excluding or including very specific content types via their type id. For example, here we create a browse filter to show all feature content except polygon feature classes using a combination of the BrowseProjectFilter.FilterFlag.FeatureClass flag and fgdb_fc_polygon type id.

 var noPolyFilter = new BrowseProjectFilter();//this will be our polygon-only filter
 //Show all feature class types using the FeatureClass flag
 noPolyFilter.AddMustBeFlag(BrowseProjectFilter.FilterFlag.FeatureClass);
 //Refine the filter to exclude polygons
 noPolyFilter.AddMustNotBeTypeId("fgdb_fc_polygon");
 noPolyFilter.Name = "No Polygons";

 //Display the filter in an Open Item dialog
 var dlg = new OpenItemDialog {
    Title = "No Polygon Feature Classes",
    InitialLocation = @"E:\Pro",
    BrowseFilter = noPolyFilter
 };
 bool? ok = dlg.ShowDialog();

BrowseFilterNoPolygons

Browse Places

Browse places are the set of available nodes shown in the treeview (in the left pane) of the Open and Save item dialogs. There are currently three available "browse places" (all shown by default): A Project, Portal (or "Online"), and Computer browse place, each of which can be expanded to browse its content. Which browse places are included "in" or excluded "from" the dialog can be controlled via the BrowseProjectFilter Includes and Excludes collections. Add the string identifier of the browse place to be included or excluded from the corresponding collection to the relevant "Includes" or "Excludes" collection (see below). Note: Browse Places are always implicitly included so "including" a browse place is, in reality, equivalent to a no-op.

The project browse place node can be further refined by including or excluding individual item containers from within it by specifying their container path or "key" in the same "Includes" or "Excludes" collections. By default, all project containers are included in the Project browse place.

Open and Save Item Dialog Browse Place Identifiers

Browse Place Identifier Notes
Project project_browse_place_project Can be customized via container keys
Portal (Online) esri_browsePlaces_Online
Computer esri_browsePlaces_Computer

In this example, the Project browse place is limited to showing just the Folders, Databases, Styles, and Servers containers. The Portal browse place has been excluded. The dialog is using a default browse project filter (no explicit filtering specified). The Project and Computer browse places did not have to be explicitly included to be shown:

  var defaultFilter = new BrowseProjectFilter();

  //Exclude Portal. Project and Computer are implicitly included.
  defaultFilter.Excludes.Add("esri_browsePlaces_Online");

  //Customize the containers shown in the Project Browse Place.
  //Listing _any_ included container restricts the Project Browse Place to
  //showing _just those explicitly included_ in the list.
  defaultFilter.Includes.Add("FolderConnection");
  defaultFilter.Includes.Add("Style");
  defaultFilter.Includes.Add("GDB");
  defaultFilter.Includes.Add("ServerConnection");

  var dlg = new OpenItemDialog {
    Title = "Customized Browse Places",
    InitialLocation = @"E:\Pro",
    BrowseFilter = defaultFilter
  };
  bool? ok = dlg.ShowDialog();

BrowsePlacesCustomized

Note: The complete list of project containers can be found in the Public Containers table in this document.

Configure Filters in DAML

BrowseProjectFilters can be declared in DAML in addition to being configured in code. A new browse filter in DAML must be registered as a component with the esri_browseFilters Pro category. The newly added browse filter component specifies:

  • A unique identifier. This will be specified as the "id" attribute. This string will be passed as a parameter to the BrowseProjectFilter constructor to create an instance of the filter.

  • A <content> child element containing:

    • displayName attribute.
    • include attribute that corresponds to the BrowseProjectFilter.Includes collection.
    • exclude attribute that corresponds to the BrowseProjectFilter.Excludes collection.
  • One or more filter configuration child elements, each of which can have multiple <type> child elements with an id attribute. These elements are used to build the "Allow" and "Deny" list of typeIDs and/or flags to display in the filter:

    • <canBeTypeIds> - TypeIDs that will be displayed by the filter.
    • <canBeFlags> - Content type flags to be displayed by the filter.
    • <mustNotBeTypeIds> - Specific TypeIDs to be excluded by the filter.
    • <mustBeFlags> - Content type flags that browsed content must be
    • <mustNotBeFlags> - Content type flags that will be excluded
    • <doBrowseIntoTypeIds> - TypeIDs that you can browse into. For example, using the typeID "raster_tiff" will allow the filter to browse into tiff images.
    • <dontBrowseIntoTypeIds> - Specific TypeIDs to prevent from being browsable.
    • <dontBrowseIntoFlags> - Content type flags of containers not to be browsed into

As an example, here is a DAML code snippet that creates a new filter to display just line feature classes within File Geodatabases only:

  <ArcGIS>
    ...
    <categories>
     <updateCategory refID="esri_browseFilters">
       <insertComponent id="NewLineFeatures_Filter">
        <content displayName="Line feature class in FGDB" include="FolderConnection, GDB"
                 exclude="esri_browsePlaces_Online">
           <canBeTypeIds>
             <type id="fgdb_fc_line"/>
           </canBeTypeIds>
           <dontBrowseIntoFlags>
             <type id="DontBrowseFiles" /><!-- note this is a flag not a type id -->
           </dontBrowseIntoFlags>
	   <doBrowseIntoTypeIds>
	      <type id="database_fgdb" />
	   </doBrowseIntoTypeIds>
	 </content>
        </insertComponent>
    </categories>
    ....

With this filter defined in the add-in Config.daml, it is available for use at anytime (by any add-in) in the Pro session. Invoke the BrowseFilterClass constructor passing in the DAML id of the custom filter as its parameter - NewLineFeatures_Filter in this case.

 var customFilterFromDaml = new BrowseProjectFilter("NewLineFeatures_Filter");
 var dlg= new OpenItemDialog {
   Title = "Open Line Feature classes",
   InitialLocation = @"C:\Data",
   BrowseFilter = customFilterFromDaml
 };
 bool? ok = dlg.ShowDialog();

LineFCOnly

An example of creating a custom filter to display your own custom items can be found in the following ProGuide: ProGuide: Custom items, step 6.

UI Controls

Sign On

The Sign On Control is a UI component similar to the core sign on UI at the top right of the the ArcGIS Pro application. Use this control within an Add-in or Configuration to allow users to sign into ArcGIS Online or a portal. If the user is already signed-in, user details are displayed.

SignOnControl

Add the control to a view

xmlns:coreControls="clr-namespace:ArcGIS.Desktop.Core.Controls;assembly=ArcGIS.Desktop.Core"

<coreControls:SignOnControl x:Name="signOn"/>

The control has a number of read-only dependency properties which can be used to display the following information

You can also listen to the SignOnStatusChanged event with the SignOnStatusChangedEventArgs containing the same information.

  signOn.SignOnStatusChanged += SignOn_SignOnStatusChanged;

  private void SignOn_SignOnStatusChanged(object sender, ArcGIS.Desktop.Core.Controls.SignOnStatusChangedEventArgs args)
  {
    var isSignedIn =  args.IsSignedIn;
    var userName = args.SignedOnUserName;
    var portal = args.CurrentPortalUri;
    var fullName = args.SignedOnFullName;
    var orgName = args.SignedOnOrganizationName;
  }

A configuration using the SignOn Control can be found at ProStartPageConfig.

Recent Projects

The Recent Projects Control is a component similar to the project list UI displayed on the Open or New Project pages of the ArcGIS Pro application. It displays the recently opened and pinned ArcGIS Pro projects. Use this control within a Configuration on a custom StartPage to replicate the standard ArcGIS Pro experience. You can also use the control with an Add-in as required.

Add the control to a view

xmlns:coreControls="clr-namespace:ArcGIS.Desktop.Core.Controls;assembly=ArcGIS.Desktop.Core"

<coreControls:RecentProjectsControl x:Name="recentProjects"                                       
                                        ViewType="List"
                                        ShowHorizontalScroll="Auto"
                                        ShowVerticalScroll="Auto" />

Customize the UI by setting the IsTitleVisible, IsFilterVisible, IsViewTypesVisible, IsSortVisible, IsBrowseProjectsVisible flags. You can also set the ViewType to be "List" or "Tile".

The list of recent and pinned projects can be retrieved by the read-only dependency properties RecentProjects and PinnedProjects.

The SelectedProjectChanged event fires when a project is selected, allowing you to specify a custom action. Typically this would be opening the selected project, but allows other actions to be specified.

RecentProjects

A configuration using the RecentProjectsControl can be found at ProStartPageConfig.

Recent Templates

The Recent Templates Control is a component similar to the template list UI displayed on the Open or New Project pages of the ArcGIS Pro application. It displays the recently used and pinned ArcGIS Pro project template. Use this control within a Configuration on a custom StartPage to replicate the standard ArcGIS Pro experience. You can also use the control with an Add-in as required.

Add the control to a view

xmlns:coreControls="clr-namespace:ArcGIS.Desktop.Core.Controls;assembly=ArcGIS.Desktop.Core"

<coreControls:RecentTemplatesControl x:Name="recentTemplates" />

Customize the UI by setting the IsTitleVisible or IsBrowseTemplatesVisible flags.

The list of recent and pinned templates can be retrieved by the read-only dependency properties RecentTemplates and PinnedTemplates

The SelectedTemplateChanged event fires when a template is selected, allowing you to specify a custom action. Typically this would be opening a project with the specified template, but allows other actions to be specified.

RecentTemplates

A configuration using the RecentTemplatesControl can be found at ProStartPageConfig.

Developing with ArcGIS Pro

    Migration


Framework

    Add-ins

    Configurations

    Customization

    Styling


Arcade


Content


CoreHost


DataReviewer


Editing


Geodatabase

    3D Analyst Data

    Plugin Datasources

    Topology

    Object Model Diagram


Geometry

    Relational Operations


Geoprocessing


Knowledge Graph


Layouts

    Reports


Map Authoring

    3D Analyst

    CIM

    Graphics

    Scene

    Stream

    Voxel


Map Exploration

    Map Tools


Networks

    Network Diagrams


Parcel Fabric


Raster


Sharing


Tasks


Workflow Manager Classic


Workflow Manager


Reference

Clone this wiki locally