ProGuide Custom Items - kataya/arcgis-pro-sdk GitHub Wiki

Language:      C#
Subject:       Map Exploration
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

This guide demonstrates how to create a Custom Project Item which is used to 'customize' ArcGIS Pro's Catalog browsing to specific file types. See ProConcepts Custom Items for more information on the Custom Item / Custom Project Item patterns.

Prerequisites

  • Download and install the sample data required for this guide as instructed in ArcGIS Pro SDK Community Samples Releases.
  • Create a new ArcGIS Pro Module Add-in, and name the project ProItemEarthQuake. If you are not familiar with the ArcGIS Pro SDK, you can follow the steps in the ProGuide Build your first add-in to get started.
  • Once the Solution is open in Visual Studio right click on the project, select Add New Item, drill down to the ArcGIS Pro Add-ins, select ArcGIS Pro Custom Project Item, and name the item QuakeProjectItem.

Note for developers

When working on Custom Items or Project Custom Items be aware that Custom Item classes are also registered for use by the ArcGIS Pro Indexer when ArcGIS Pro starts. This registration is done through an entry in this file:

%AppData%..\Local\ESRI\SearchResources\ItemInfoTypesExt.json

Usually this is of no consequence, but it is advisable to delete this file in-between sessions when you are developing your Custom Item and when you are refactoring your code.

Step 1
Modify the fileExtension to customize browsing to a specific file extension

Modify the Config.daml ProItemEarthQuake_Module module:

  • Delete the <tabs> and <groups> sections inside the <insertModule> tag.
  • Find the fileExtension attribute and change the value to quake.
...
<content displayName="QuakeProjectItem" keywords="QuakeProjectItem" 
		 fileExtension="quake" 
		 isContainer="false" 		 
		 contextMenuID="ProItemEarthQuake_QuakeProjectItem_ContextMenu" />
...

Build the sample and start debugging. Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample.
After the project has been loaded, open the ArcGIS Pro Catalog Dockpane, and drill down to Folders | QuakeCustomItem | earthquakes.quake. earthquakes.quake should display the Bex the dog icon.

ProGuide Step 1

Step 2
Add Child records (Quake Events) to a Custom Project Item

earthquakes.quake contains an XML document containing QuakeEvents. We will add these QuakeEvents as children to QuakeCustomItem. Open the source for QuakeProjectItem. To indicate that QuakeProjectItem has children change IsContainer to true.

public override bool IsContainer => true;

Add the following class to the QuakeProjectItem source. This class represent one single earthquake event or a single child of QuakeProjectItem.

/// <summary>
/// Quake event items. These are children of a QuakeProjectItem
/// </summary>
/// <remarks>QuakeEventProjectItems are, themselves, custom items</remarks>
internal class QuakeEventProjectItem : CustomProjectItemBase
{
  public QuakeEventProjectItem(string name, string path, 
  					string type, string lastModifiedTime) 
  					: base(name, path, type, lastModifiedTime)
  {
  }
  
  public void SetNewName(string newName)
  {
  	this.Name = newName;
  	NotifyPropertyChanged("Name");
  	this.Title = newName;
  	NotifyPropertyChanged("Title");
  	this._itemInfoValue.name = newName;
  	this._itemInfoValue.description = newName;
  }
  
  public override ProjectItemInfo OnGetInfo()
  {
  	var projectItemInfo = new ProjectItemInfo
  	{
  		Name = this.Name,
  		Path = this.Path,
  		Type = QuakeProjectItemContainer.ContainerName
  	};
  	return projectItemInfo;
  }
  
  public override ImageSource LargeImage
  {
  	get
  	{
  		return System.Windows.Application.Current.Resources["T-Rex32"] 
  			as ImageSource;
  	}
  }
  
  public override Task<ImageSource> SmallImage
  {
  	get
  	{
  		ImageSource img = System.Windows.Application.Current.Resources
  						["T-Rex16"] as ImageSource;
  		return Task.FromResult(img);
  	}
  }
}

Next the Fetch method in QuakeProjectItem is getting replaced with a new version that opens the quake file and parses it's XML content to provide the list of QuakeEventProjectItem children for QuakeProjectItem.

/// <summary>
/// Fetch is called if <b>IsContainer</b> = <b>true</b> and the project item is being
/// expanded in the Catalog pane for the first time.
/// </summary>
/// <remarks>The project item should instantiate items for each of its children and
/// add them to its child collection (see <b>AddRangeToChildren</b>)</remarks>
public override void Fetch()
{
  //This is where the quake item is located
  string filePath = this.Path;
  //Quake is an xml document, we parse it's content to provide the list of children
  XDocument doc = XDocument.Load(filePath);
  
  XNamespace aw = "http://quakeml.org/xmlns/bed/1.2";
  IEnumerable<XElement> earthquakeEvents = from el in 
  				doc.Root.Descendants(aw + "event") select el;
  
  List<QuakeEventProjectItem> events = new List<QuakeEventProjectItem>();
  var existingChildren = this.GetChildren().ToList();
  int event_count = 1;
  //Parse out the child quake events
  foreach (XElement earthquakeElement in earthquakeEvents)
  {
    // the path has to be unique for each QuakeEventProjectItem
    var uniquePath = filePath + $"[{event_count}]";
    XElement desc = earthquakeElement.Element(aw + "description");
    XElement name = desc.Element(aw + "text");
    string fullName = name.Value;
    // prevent duplicate entries
    if (existingChildren.Any(i => i.Path == uniquePath)) continue;
    
    XElement origin = earthquakeElement.Element(aw + "origin");
    XElement time = origin.Element(aw + "time");
    XElement value = time.Element(aw + "value");
    string date = value.Value;
    DateTime timestamp = Convert.ToDateTime(date);
    
    //Make a "QuakeEventProjectItem" for each child read from the quake file
    QuakeEventProjectItem item = new QuakeEventProjectItem(
      fullName, uniquePath, "quake_event", timestamp.ToString());
    
    events.Add(item);
    event_count++;
  }
  //Add the event "child" items to the children collection in QuakeProjectItem
  this.AddRangeToChildren(events);
}

Step 3
Verify that Custom Project Item with child records works

Rebuild the add-in. Fix any compilation errors. Run the Add-in in debug mode. Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample.
After the project has been loaded, open the ArcGIS Pro Catalog Dockpane, and drill down to Folders | QuakeCustomItem | earthquakes.quake.
earthquakes.quake should display the Bex the dog icon. Once to drill down to earthquakes.quake you should now see all children with the T-Rex icon.

ProGuide Step 3

Step 4
Add Custom Project Item to project

In this step we verify that we can add the earthquakes.quake Custom Project Item as a Project Item to the project. The ProItemEarthQuake_QuakeProjectItem_ContextMenu context menu which is referenced and defined in config.daml allows to trigger the AddToProject OnClick method. The AddToProject code behind is used to add the Custom Project Item to a QuakeProjectItemContainer. The code for this has already been stubbed out by the Custom Project Item Item Template we used early.

internal class AddToProject : Button
{
  protected override void OnClick()
  {
    var catalog = Project.GetCatalogPane();
    var items = catalog.SelectedItems;
    var item = items.OfType<QuakeProjectItem>().FirstOrDefault();
    if (item == null)
    	return;
    QueuedTask.Run(() => Project.Current.AddItem(item));
  }
}

As can be seen in the code snippet above the current selected QuakeProjectItem is simply added the Project Items of the current project.

In order to support the save/restore operation for QuakeProjectItems you have to uncomment the already stubbed out TODO item for the following QuakeProjectItem constructor:

// Overload for use in your container create item
public QuakeProjectItem(string name, string catalogPath, string typeID, string ontainerTypeID)
					: base(name, catalogPath, typeID, containerTypeID)
{
}

Build the sample and start debugging. Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample.
After the project has been loaded, open the ArcGIS Pro Catalog Dockpane, and drill down to Folders | QuakeCustomItem | earthquakes.quake. earthquakes.quake should display the Bex the dog icon, right click on this item to bring up the context menu and then click on Add To Project to add the Item to the current project.

ProGuide Step 4

ProGuide Step 4 b

Step 5
Add support for renaming of Custom Project Items

In order to support renaming two overrides in QuakeProjectItem have to be added:

  1. The CanRename property has to be set to true
  2. The OnRename method has be overridden to implement any custom actions to support renaming.

Add the following Rename override code to your QuakeProjectItem class:

#region Rename override code

protected override bool CanRename => true;

protected override bool OnRename(string newName)
{
	// have to do some work to actually change the name so if they call refresh it'll be here
	// whether it's a file on disk or node in the xml
	var new_ext = System.IO.Path.GetExtension(newName);
	if (string.IsNullOrEmpty(new_ext))
	{
		new_ext = System.IO.Path.GetExtension(this.Path);
		newName = System.IO.Path.ChangeExtension(newName, new_ext);
	}
	var new_file_path = System.IO.Path.Combine(
	  System.IO.Path.GetDirectoryName(this.Path), newName);
	System.IO.File.Move(this.Path, new_file_path);
	this.Path = new_file_path;
	return base.OnRename(newName);
}

#endregion Rename override code

To rename a Custom Project Item from within the ArcGIS Pro Catalog pane we can simply use the existing out-of-box renaming button esri_core_rename of ArcGIS Pro and add the esri_core_rename button to the Custom Project Item's context menu. This is solely done in DAML, the snippet below shows the addition of the rename button to the MyCustomItem Context Menu:

<menu id="ProItemEarthQuake_QuakeProjectItem_ContextMenu" caption="QuakeProjectItem_Menu">
  <button refID="ProItemEarthQuake_QuakeProjectItem_AddToProject" />
  <button refID="esri_core_rename" />
</menu>

Build the sample and start debugging. Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample.
After the project has been loaded, open the ArcGIS Pro Catalog Dockpane, and drill down to Folders | QuakeCustomItem | earthquakes.quake. Rename earthquakes.quake by using the rename Context Menu button or by simply clicking on the Custom Project Item name to enable editing of the name.

Custom Project Item Rename Context Menu

Custom Project Item Renaming

Step 6
Add support for browsing for custom project items

In this step we will be creating a BrowseProjectFilter to configure the OpenItemDialog to browse to ".quake" custom project items, expand it, and list the child quake event custom items. Refer to Browse dialog filters for more information on creating and configuring item filters.

In the Visual Studio solution explorer, right click on the project node and add an ArcGIS Pro Button item. In the add item dialog, name this button class file as QuakeFilterDialog. QuakeFilterDialog.cs file will be created. This button will display Pro's OpenItemDialog and allow you to filter for the ".quake" files.

In the OnClick method that is stubbed out, add the following code. We first create the BrowseProjectFilter instance and then set the filter properties for this class using the AddCanBeTypeId method. acme_quake_handler is passed to the AddCanBeTypeId method as the typeID for the "quake" item. We then create the OpenItemDialog and set the BrowseFilter property to the BrowseProjectFilter instance created.

protected override void OnClick() {
  var bf = new BrowseProjectFilter();
  //This allows us to view the .quake custom item (the "container")
  bf.AddCanBeTypeId("acme_quake_handler"); //TypeID for the ".quake" custom project item        
  bf.Name = "Quake Item";

  var openItemDialog = new OpenItemDialog {
     Title = "Open Quake Item",
     InitialLocation = @"C:\Data\CustomItem\QuakeCustomItem",
     BrowseFilter = bf
  };
  bool? ok = openItemDialog.ShowDialog();
}

Rebuild the add-in solution. Run the add-in in debug mode.

Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample. Click the Add-In tab and click the newly added QuakeFilterDialog button. The OpenItemDialog will display. Browse to C:\Data\CustomItem\QuakeCustomItem folder if needed. You will see the .quake item displayed.

ProGuide Step 6 a

Since the quake item is a "container" that holds the individual quake events, we can modify the filter behavior to allow browse into the custom quake project item and display the quake events in the container.

First, we use the AddDoBrowseIntoTypeID method on the BrowseProjectFilter instance and pass in the acme_quake_handler typeID of the quake item. This enables browsing into the .quake event. Additionally, to allow us to view the individual events within the "quake" container, we add the acme_quake_event TypeID (for the quake events) using AddCanBeTypeId.

 //Enable browsing of the .quake item to access the events inside
 bf.AddDoBrowseIntoTypeId("acme_quake_handler");
 //This allows the quake events contained in the .quake item to be shown
 bf.AddCanBeTypeId("acme_quake_event");

The entire OnClick method is shown below.

protected override void OnClick()
{
  var bf = new BrowseProjectFilter();
  //Enable browsing of the .quake item to access the events inside
  bf.AddCanBeTypeId("acme_quake_handler");
  //This allows the .quake item to be browsable to access the events inside
  bf.AddDoBrowseIntoTypeId("acme_quake_handler");
  //This allows the quake events contained in the .quake item to be shown
  bf.AddCanBeTypeId("acme_quake_event"); //TypeID for the quake events contained in the .quake item
  bf.Name = "Quake Item";

  var openItemDialog = new OpenItemDialog {
    Title = "Open Quake Item",
    InitialLocation = @"C:\Data\CustomItem\QuakeCustomItem",
    BrowseFilter = bf
  };
  bool? ok = openItemDialog.ShowDialog();
}

Rebuild the add-in solution. Run the add-in in debug mode.

Open the C:\Data\CustomItem\QuakeCustomItem\QuakeCustomItem.aprx project sample. Click the Add-In tab and click the newly added QuakeFilterDialog button. The OpenItemDialog will display. Browse to C:\Data\CustomItem\QuakeCustomItem folder if needed.

You will see the .quake item displayed. Double click the .quake item to display its contents. You will see the quake event child items displayed in the browse dialog.

ProGuide Step 6 b

A sample using the Custom Project Item implementation can be found at Project Item EarthQuake.

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