Skip to content

ProConcepts Content and Items

arcgisprosdk edited this page Jun 23, 2017 · 20 revisions

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

Language:      C# and Visual Basic
Subject:       Content
Contributor:   ArcGIS Pro SDK Team <[email protected]>
Organization:  Esri, http://www.esri.com
Date:          6/13/2017  
ArcGIS Pro:    2.0  
Visual Studio: 2015, 2017

In this topic

Overview

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").

*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:

Class Namespace Assembly
FolderConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
GDBProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.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
LocatorsConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
MapProjectItem ArcGIS.Desktop.Mapping ArcGIS.Desktop.Mapping.dll
ReviewerBatchJobProjectItem ArcGIS.Desktop.DataReviewer ArcGIS.Desktop.DataReviewer.dll
ReviewerResultsProjectItem ArcGIS.Desktop.DataReviewer ArcGIS.Desktop.DataReviewer.dll
StyleProjectItem ArcGIS.Desktop.Mapping ArcGIS.Desktop.Mapping.dll
ServerConnectionProjectItem ArcGIS.Desktop.Catalog ArcGIS.Desktop.Catalog.dll
TaskProjectItem ArcGIS.Desktop.TaskAssistant ArcGIS.Desktop.TaskAssistant.dll

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));

Note: 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".

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, 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
  }

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

Getting Item Content

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. 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 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.

Note: 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.

Catalog Context

The context (i.e. "what is selected") of both the Catalog Pane and the Catalog View can be accessed via the ArcGIS.Desktop.Core.IProjectWindow interface whenever either the Catalog Pane or View has focus (see topic 14659).

To get the catalog context, first retrieve the active window from the framework application. This will be whichever dockpane or view pane currently has focus. Cast the window to IProjectWindow (using "as"). If the active window is either the Catalog Pane or a Catalog View, the cast will succeed otherwise the window will be null. What is currently selected is accessed off the IProjectWindow.SelectedItems member.

var window = FrameworkApplication.ActiveWindow as ArcGIS.Desktop.Core.IProjectWindow
var item = window?.SelectedItems.First();
//do something with the selection...

Note: 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.)

Developing with ArcGIS Pro

    Migration


Framework

    Add-ins

    Configurations

    Customization

    Styling


Arcade


Content


CoreHost


DataReviewer


Editing


Geodatabase

    3D Analyst Data

    Plugin Datasources

    Topology

    Linear Referencing

    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