ProGuide Content and Image Resources - kataya/arcgis-pro-sdk GitHub Wiki

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

Often you need to include content and resources within your add-in. For example, you need to distribute a PDF or text file with your add-in, or your controls and custom UI will commonly reference image content. To include content in your ArcGIS Pro add-in, follow the steps shown in these examples:



Image content

This can be a confusing topic given that there are so many different options. In general, follow these guidelines:

  • If you have an image control or image source property on a WPF control, use Resource.
  • If you have a control declared in the Config.daml that uses an image (for example, a button), use AddinContent.

Embedded resources are not really useful for ArcGIS Pro add-ins. However, if you need to use them, refer to Images as Embedded Resources. If you want to use an image from the ArcGIS Pro UI in your own add-in, refer to Referencing Images from ArcGIS Pro.

Images as AddinContent

AddinContent is a custom Build Action introduced to use with add-ins. It is exclusively intended for image resources referenced by Addin controls in DAML (not WPF controls in XAML).

Because of the delay load capability of add-ins, the images associated with a button, tool, menu item, and so on, on the Pro Ribbon, need to be shown on the UI before the add-in assembly that contains them is loaded (images are accessed in WPF from resources in a loaded assembly).

Resources with a Build Action of AddinContent are added to your add-in archive root when your add-in is built. They are accessed directly from within the add-in archive by Pro so that the add-in assembly does not have to be loaded to render the add-ins on the UI.

To use AddinContent:

  1. Set the Build Action to AddinContent. Set Copy to Output Directory to Do not copy.
  2. Specify the relative path to the image resource in your DAML. Because you are specifying a path to a resource in the add-in archive, you must use "\" (backslash) as the path separator (and not "/" (forwardslash) as with pack URI syntax.

For example, given an image AddinDesktop32.png in a project folder called Images, to reference it in DAML:

<!-- Note the backslash -->
<button id="acme_module1_Button1" className="Button1" caption="Button1"
               largeImage="Images\AddinDesktop32.png"/>

To access in code:

using System.Windows.Media.Imaging;
using System.IO.Compression;

//Image is defined as AddinContent.
//AddinContent is ESRI custom build action. Cannot use without Addin
BitmapImage forAddinContent = new BitmapImage();
string id = "{49545510-2f4a-4e9c-bc12-3ea79a330fa0}";//From your module
using (ZipArchive zip = ZipFile.OpenRead(
     @"C:\Users\my_username\Documents\ArcGIS\AddIns\ArcGISPro\" + id + 
     @"\AddinContent.esriAddInX")) {
	ZipArchiveEntry zipEntry = zip.GetEntry("Images\AddInDesktop32.png");
	MemoryStream ms = new MemoryStream();
	using (Stream stmZip = zipEntry.Open()) {
		stmZip.CopyTo(ms);
	}
	
	forAddinContent.BeginInit();
	forAddinContent.StreamSource = ms;
	forAddinContent.EndInit();
}

ZipArchive and ZipArchiveEntry can be found in System.IO.Compression. Add references to System.IO.Compression.dll and System.IO.Compression.FileSystem.dll.

Using image as resource for add-in control content

If you do want to use an image in your assembly with a Build Action of Resource and not AddinContent as the image for a button, tool, and so on, on the Pro Ribbon, your add-in assembly must load at startup (and not delay load). Set the insertModule autoLoad attribute to true in your Config.daml (the default is false for delay loading).

<!-- in your Config.daml: note autoLoad attribute -->
<insertModule id="ProModule_Module1" 
     className="Module1" autoLoad="true" caption="Module1">

<!-- now you can use a pack uri on your control DAML declaration -->
<button id="ProModule_Button1" caption="Button1" className="Button1" loadOnClick="true"
      largeImage="pack://application:,,,/Module1;component/images/AddinDesktop32.png"/>

Images as resources

Add the image to your Visual Studio project and:

  1. Set its Build Action to Resource.
  2. Set the Copy to Output Directory property to Do not copy.

(Refer to MSDN for more information on Build Action settings for images.)

Image resources can be accessed in code or in XAML. For example, given an image called Resource32.png in a project folder called Images in an add-in assembly called Module1.dll. To access:

In code:

BitmapImage fromResource = new BitmapImage(new Uri(
  "pack://application:,,,/Module1;component/Images/Resource32.png",UriKind.Absolute));

In XAML:

<!-- Assume we have an Image Control -->
<Image Stretch="None" 
       Source="pack://application:,,,/Module1;component/Images/Resource32.png"/>

Usually, an image source in your add-in's custom UI binds to an ImageSource property on a view model. Assuming you have a view model set on the DataContext of your window or user control, then:

public class MyViewModel : INotifyPropertyChanged {
 private BitmapImage _img = null;

 public ImageSource ContentImageSource {
   get {
     if (_img == null)
        _img = new BitmapImage(new Uri(  
          "pack://application:,,,/Module1;component/Images/Resources32.png",UriKind.Absolute));
     return _img;
  }
}    

In the control XAML:

<Image Stretch="None" Source="{Binding Path=ContentImageSource}"/>
Using images from a resource dll

When you have many image resources in your add-in development, you can create a separate project in your add-in solution ("resource dll") that holds all these image resources.

Images referenced using the Pack URI from this resource dll will display only when the dll is loaded and in memory. If the dll is not loaded, the resource dictionary that Pro has in memory does not contain your image. There are 2 ways to load the resource dll when Pro launches in order to access these images within. In both cases, you must set the insertModule autoLoad attribute to true in your Config.daml (the default is false for delay loading).

  1. Create a class in your resource dll that you instantiate in your add-in's module class. This will load the resource dll when the add-in is loaded, which will allow the image to be displayed.

  2. In your module class's initialize override, load the resource assembly using the code snippet below.

// In Add-in's module class:

protected override bool Initialize()
{
   Load();
   return base.Initialize();
}
private void Load()
 {
   string assemblyName = "ResourceDllWithUserControl";
   string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
   string assemblyPath = Path.Combine(folderPath, string.Format("{0}.dll", new AssemblyName(assemblyName).Name));
   Assembly assembly = Assembly.LoadFrom(assemblyPath);
}

Referencing images from ArcGIS Pro

ArcGIS Pro icons are stored within the ArcGIS.Desktop.Resources.dll and are loaded, at startup, by the Pro application. By default, the Pro SDK item templates that generate ribbon content (such as buttons and tools) reference the default add-in images from the ArcGIS.Desktop.Resources.dll. Prior to 2.0, the default add-in images (such as GenericButtonBlue32.png, GenericButtonRed32.png, and so forth) were physically deployed, within the Images folder, as a component of the add-in with the build action set to "AddinContent". The advantage to AddinContent is that images with this build action could be referenced within the Config.daml without the need to load the add-in dll. However, as ArcGIS.Desktop.Resources.dll is always loaded at start-up, the need to use AddinContent is negated (in this case). Additionally, the pack Uri reference will work equally within the Config.daml or within a WPF User Control - an added benefit over the AddinContent type.

The complete Pro icon reference can be found here: DAML ID Reference Icons and also includes the Pro SDK Icons as well. The generic pack Uri syntax to use when referencing the ArcGIS.Desktop.Resources.dll is:

<Button id="acme_module1_Button1" className="Button1" caption="Button1"
 largeImage="pack://application:,,,/ArcGIS.Desktop.Resources;component/Images/GenericButtonBlue32.png">

where ArcGIS.Desktop.Resources is the name of the resource assembly (Pro's resource assembly in this case), Images/ is the path component to their location in the resource assembly and GenericButtonBlue32.png is the name of the image being referenced. A complete guide to the pack Uri syntax is available here: Pack URIs in WPF.

Here is another example referencing the "ColorSelector" image (used by the color selector button in the mapping module) within the Config.daml:

<!-- Use a Pro image for a custom Add-in button -->
<button id="acme_module1_Button1" className="Button1" caption="Button1"
 largeImage="pack://application:,,,/ArcGIS.Desktop.Resources;component/Images/ColorSelector32.png"/>

As mentioned, unlike add-in image content, the Pro image resources are loaded at startup; therefore, you can safely use a pack URI to reference them and the same pack Uri can be used to reference Pro Image resources within your WPF .xaml content as well.

Images as embedded resources

Embedded resources are really designed to use with Windows and System.Drawing.Bitmap, whereas resources are designed to use with WPF and System.Windows.Media.Imaging.BitmapImage. In Arcobjects extensions, you will have used Embedded Resources extensively but in Pro, you will instead be working mostly with Resources.

If you do need to use embedded resources with Pro, you can only access them in code. For example, given an an image called EmbeddedResource32.png in a project folder called Images that is an embedded resource:

  //Image is defined as Embedded Resource. Copy to Output Directory = Do not copy
  var asm = System.Reflection.Assembly.GetExecutingAssembly();
  var stm = asm.GetManifestResourceStream(this.GetType(),"Images.EmbeddedResource32.png"));
  BitmapImage embeddedResource = new BitmapImage();
  embeddedResource.BeginInit();
  embeddedResource.StreamSource = stm;
  embeddedResource.EndInit();


 



Document content

To deploy file content with your add-in:

  1. Set its Build Action = Content or Build Action = None (makes no difference which).
  2. Set Copy to Output Directory = Copy always.
Build Action None Build Action Content

Add-in file contents are copied to a temporary output folder (the "assembly cache") by the ArcGIS Pro framework at runtime when the add-in loads. Any folder hierarchy within your add-in's Visual Studio project containing content is preserved in the output.

Accessing document content

To locate the document content, get the path to the add-in assembly location at runtime and append to it the relative path to the document content to be accessed.

Step 1: To get the location of your assembly use:

string uri = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;

You can also use this handy function:

public static string AddinAssemblyLocation() {
    var asm = System.Reflection.Assembly.GetExecutingAssembly();
    return System.IO.Path.GetDirectoryName(
                      Uri.UnescapeDataString(
                              new Uri(asm.CodeBase).LocalPath));
}

Step 2: Append the relative path to the document content. For example, assume you have a content file called Screenshot_doc.docx in a visual studio project folder called ThisIsContent that has been deployed as content:

  string contentPath = System.IO.Path.Combine(
           AddinAssemblyLocation(), "ThisIsContent", "Screenshot_doc.docx");
  //open it
  ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(contentPath);
  psi.UseShellExecute = true;
  Process.Start(psi);


 



Embedding Toolboxes

Toolboxes(.tbx) and Python Toolboxes (.pyt) files to include supporting scripts and/or python modules and any other associated content (help, messages, metadata, etc) can be included within an add-in for deployment to the ArcGIS system toolboxes.

Assuming your Add-in project is open within Visual Studio:

  1. Right-click and add a "New Folder" to your Visual Studio add-in project
  2. Name the folder "Toolboxes"

Toolboxes folder

  1. Add any required subfolders to the Toolboxes folder. For example, subfolders for a toolbox, a python toolbox, a python module used by the toolbox, and/or any other supported files used by the toolbox. Refer to Extending Geoprocessing Through Python Modules for some examples. Also, this Geonet post may prove useful on getting the add-in folder structure correct: Embedding Toolboxes in ArcGIS Pro

  2. Right-click on the Toolboxes folder, or any of its sub-folders, select Add->Existing Item

  3. Browse to the Toolbox(es) and supporting file content you wish to embed in your add-in within the Toolboxes folder and each respective subfolder if there are any.

Note: use "Add as Link" to add a link to a file in its original location rather than making a copy within the Visual Studio project folder hierarchy.

AddAsLink

  1. Set the Build Action for your content files to "AddinContent"

AddinContent

  1. Build your add-in project
  2. The Toolboxes folder and content marked as "AddinContent" (to include sub-folders containing "AddinContent") will be zipped into the .esriAddinX archive. The Toolboxes folder is added to the root-level of the archive.
  3. Deploy your add-in
  4. Start pro to load your add-in
  5. A Toolboxes folder is created at \Users\<username>\AppData\Local\ESRI\ArcGISPro\Toolboxes if not previously created.
  6. Toolboxes will contain Guided subfolder names corresponding to the add-in ids of add-ins loaded into Pro for the current session that contain "Toolboxes" folders. The contents of the Toolboxes folders will have been copied into the respective Guided subfolders.

Toolboxes folder2

  1. When GP initializes for the current session, all toolboxes probed in the AppData Toolboxes folder will be added to the collection of available system toolboxes for Pro.

Note: whenever Pro starts up, Toolbox folders associated with add-ins that are not loaded (due to security settings, they were uninstalled, etc) are deleted. Only add-ins loaded for the current session have a Toolboxes folder deployed.

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