Skip to content

ProGuide Content and Image Resources

UmaHarano edited this page Nov 6, 2023 · 23 revisions
Language:      C#
Subject:       Framework
Contributor:   ArcGIS Pro SDK Team <arcgisprosdk@esri.com>
Organization:  Esri, http://www.esri.com
Date:          10/02/2023
ArcGIS Pro:    3.2
Visual Studio: 2022

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

In general, follow these guidelines:

  • To use a "stock" image from the ArcGIS Pro UI in your own add-in, depending if you reference an ArcGIS Pro image from config.daml, XAML or code-behind use the appropriate syntax to link the icon. This works equally for either ribbon or WPF (e.g. a dockpane) content.
  • To use a custom image deployed in the addin, use build action Resource and its pack uri. Set the module autoLoad attribute to true.

Each of these options is described further.

Referencing Images From ArcGIS Pro

Starting at 3.1, all ArcGIS Pro icons (images) are also available in XAML format. This is to provide support for 4K+ monitors where PNGs on high resolution monitors may appear (slightly) pixelated whereas vector (xaml) images do not. XAML images are provided for both light and dark Pro themes. 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 (e.g. for buttons and tools) reference the default add-in images from the ArcGIS.Desktop.Resources.dll

In order to reference any of these ArcGIS Pro images from within a config.daml file, the following image reference syntax has to be used:

Note that only the icon/image name needs to be specified without the need of the classic '.png' suffix.

<!-- This syntax is the only supported format starting with 4.0 -->
<Button id="acme_module1_Button1" className="Button1" caption="Button1"
 largeImage="GenericButtonBlue32" smallImage="GenericButtonBlue16" />

Here is another example where an addin button is using the Pro "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="ColorSelector32" largeImage="ColorSelector16" />

Referencing Pro Image resources directly from within WPF (user control) .xaml content can only be done by binding to a custom 'ContentImageSource' property in the view model:

<Button  Style="{DynamicResource Esri_Button}" ...>
  <Button.Content>
   <StackPanel Orientation="Horizontal">
     <Image Source="{Binding ContentImageSource}"  ... />
     <TextBlock ... Text="Settings" ... />
   </StackPanel>
  </Button.Content>
</Button>

and the code-behind in the view model:

public class Dockpane1ViewModel : Dockpane, ... {
 private ImageSource _img = null;

 //retrieve the image source for the relevant pack Uri
 public ImageSource ContentImageSource {
   get {
     if (_img == null)
        _img = System.Windows.Application.Current.Resources["ColorSelector32"] as ImageSource;
     return _img;
  }
}    

Referencing Custom Images From Your Addin

To add your own image content into the addin, use a Build Action of Resource and reference it, either in the Config.daml or in custom UI content using a pack Uri (as before). Additionally, to use custom image content on the ribbon you must set the insertModule autoLoad attribute to true in your Config.daml* (the default is false for delay loading). The pack Uri will be referencing the custom image content deployed in the addin rather than a Pro image resource (as was the case in the previous section).

*autoLoad=true is only needed for use with custom image pack Uris in the Config.daml.

In this example, assume the custom image "AddinDesktop32.png" has been added to an "Images" folder in the addin project. Its Build Action is "Resource" and the module autoLoad attribute is set to true. The pack Uri is being used for the largeImage attribute of a button on the Pro ribbon:

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

<!-- Use a pack uri on the relevant control DAML declaration -->
<button id="ProModule_Button1" caption="Button1" className="Button1" loadOnClick="true"
      largeImage="pack://application:,,,/Module1;component/Images/AddinDesktop32.png"/>

Same as with Pro images, custom image resource content can also be accessed in code. In this example, a view WPF image element is binding to a custom property in the view model that provides the relevant image source (loaded via the pack Uri):

<!-- Assume we have an Image Control -->
<Image Source="{Binding ContentImageSource }" ... />

and in the view model:

public class Dockpane1ViewModel : Dockpane, ... {
 private BitmapImage _img = null;

 //retrieve the image source for the relevant pack Uri
 public ImageSource ContentImageSource {
   get {
     if (_img == null)
       _img = new BitmapImage(new Uri(
           "pack://application:,,,/Module1;component/Images/AddinDesktop32.png",UriKind.Absolute));
     return _img;
  ...

When referencing an Add-in Image Resource directly from your view (XAML) you can also use the pack Uri syntax to access the add-in resource. Note that the image resource has to have the Build Action set to 'Resource':

<Button Grid.Row="0" Grid.Column="1" Margin="5" Width="120" VerticalAlignment="Top" 
        HorizontalContentAlignment="Right" Style="{DynamicResource Esri_Button}">
  <Button.Content>
    <Border>
      <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
        <Image Source="pack://application:,,,/<name of your add-in assembly>;component/Images/AddInDesktop32.png" 
                Margin="5" HorizontalAlignment="Center" Width="32" Height="32"></Image>
        <TextBlock Text="Browse" VerticalAlignment="Center" Margin="10,5,5,5" />
      </StackPanel>
    </Border>
  </Button.Content>
</Button>

Using Images From a Custom Resource DLL

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

Images referenced using the Pack URI from a custom 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 will not contain your image(s). There are 2 ways to load a custom resource dll deployed with the addin when Pro launches. In both cases, set the insertModule autoLoad attribute to true in the Config.daml of the addin.

  1. Either create a public class in the custom resource dll that can be instantiated by the add-in's module class. Instantiating the public class will trigger the load of the custom resource dll and will allow its images to be displayed.
internal class Module1 : Module {
   //create a class instance from the resource dll when the module is loaded
   //"Class1" is a public class defined in the ResourceDLL. It needs no functionality as such.
   internal Module1() {
      var x = new MyResourceDLL.Class1();//loads the resource assembly
  1. Or, in your module class's initialize override, load the resource assembly directly using code similar to the snippet below.
// In Add-in's module class:
protected override bool Initialize()
{
   Load();
   return base.Initialize();
}

private void Load()
 {
   string assemblyName = "MyResourceDLL";
   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);
}

In both cases, ensure that the resource assembly is copied to the addin assembly cache at runtime. Add the resource assembly as a reference to the addin and set its "CopyLocal" attribute to "Yes".

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. To use a System.Drawing.Bitmap with Pro 3.0 and .NET 6, you will need the Microsoft Windows Compatibility Pack nuget added to your add-in. Consult the Pro 3.0 Migration guide for more details.

As an example, embedded resources may still be required if you are using them to embed 32 bit .cur and .ico files into your add-in assembly. In this example, given an an image called EmbeddedResource32.png in a project folder called Images that is an embedded resource:

  //make sure the Microsoft Windows Compatibility Pack nuget has been referenced in the 
  //add-in. See the 3.0 migration guide
  //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);

3rd Party Assemblies

There may be instances when you need 3rd party assemblies to be deployed to the addin assembly cache (eg they may be dependencies required by certain of your components). If they are required for compilation then they should be added as references to the project and CopyLocal set to true.

Note: If you do not need assembly references to be deployed to the assembly cache then CopyLocal should always be set to false (this is the default for all Pro assemblies, as an example).

However, there may be cases where you still need to deploy an assembly because it is needed at runtime but is not needed for addin compilation (e.g. it is a native dll dependency). In this case, add the relevant dll to the addin project as Content using the same procedure as was described for file content above, namely:

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

This will deploy the assembly to the addin assembly cache (same as assembly references using CopyLocal=true) only it will not be used during addin compilation.

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 Esri Community 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 "Content"

Toolbox Content

  1. Build your add-in project
  2. The Toolboxes folder and content marked as "Content" (to include sub-folders containing "Content") 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 "Guid-ed" 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 "Guid-ed" 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.

AddinContent

As of Pro 3.0, the custom build action "AddinContent" is no longer supported. Instead, use the built-in content type "Content" instead. For all intents and purposes, marking an image, document, text file, etc. as build action "Content" does the same thing (as AddinContent did back at 2.x). Refer to ProConcepts-3.0-Migration-Guide, AddinContent

Developing with ArcGIS Pro

    Migration


Framework

    Add-ins

    Configurations

    Customization

    Styling


Arcade


Content


CoreHost


DataReviewer


Editing


Geodatabase

    3D Analyst Data

    Plugin Datasources

    Topology

    Object Model Diagram


Geometry

    Relational Operations


Geoprocessing


Knowledge Graph


Layouts

    Reports


Map Authoring

    3D Analyst

    CIM

    Graphics

    Scene

    Stream

    Voxel


Map Exploration

    Map Tools


Networks

    Network Diagrams


Parcel Fabric


Raster


Sharing


Tasks


Workflow Manager Classic


Workflow Manager


Reference

Clone this wiki locally