ProConcepts Content and Items - kataya/arcgis-pro-sdk GitHub Wiki
This ProConcepts will go cover content in Pro, its usage, and relevant API functionality.
Language: C#
Subject: Content
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 11/24/2020
ArcGIS Pro: 2.7
Visual Studio: 2017, 2019
In this topic
- Overview
- Project Content
- Portal Content
- External Content
- ItemFactory and AddItem
- ItemFactory and Import
- Items and Factories
- Getting Item Content
- Item Metadata
- Catalog Context
- Browse Dialog Filters
In Pro, content is represented as Items (Item topic 9110). 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, 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:
Class | Namespace | Assembly |
---|---|---|
FolderConnectionProjectItem | ArcGIS.Desktop.Catalog | ArcGIS.Desktop.Catalog.dll |
GDBProjectItem | ArcGIS.Desktop.Catalog | ArcGIS.Desktop.Catalog.dll |
LocatorsConnectionProjectItem | ArcGIS.Desktop.Catalog | ArcGIS.Desktop.Catalog.dll |
ServerConnectionProjectItem | ArcGIS.Desktop.Catalog | ArcGIS.Desktop.Catalog.dll |
StatisticalDataCollectionProjectItem | 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 |
HistoryProjectItem | ArcGIS.Desktop.GeoProcessing | ArcGIS.Desktop.GeoProcessing.dll |
LayoutProjectItem | 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 |
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: Anglular 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
}
...
});
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.
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);
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.
//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) {
...
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.
//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) {
...
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.
Creating and adding content to a project is a three step process:
-
Create a content item using
ItemFactory.Instance.Create
using the path or url to the content (whether on disk or online). -
Test the returned content for
IProjectItem
* to see if it can be added to the project. -
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! --
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
}
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
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 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.)
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.
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). SpecifycreateIfNecessary=false
if you do not the catalog pane created if it has not been created previously. -
FrameworkApplication.ActiveWindow
and test it forIProjectWindow
. 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) {
...
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;
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.
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 |
Internal Containers (non-visible)
DisplayUnitEnvironment | DisplayUnitEnvironment | Internal use |
FindProviderSettings | FindProviderSettings | Internal use |
Geoprocessing History | GPHistory | Internal use |
Ortho Mapping | OrthoMappingWorkspace | Internal use |
Reports | Report | Internal use |
Raster Function History | RFxHistory | Internal use |
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);
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
});
...
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();
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):
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:
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:
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();
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.
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();
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();
Note: The complete list of project containers can be found in the Public Containers table in this document.
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();
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.