ProGuide content and image resources - Esri/arcgis-pro-sdk GitHub Wiki
Language: C#
Subject: Framework
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 10/06/2024
ArcGIS Pro: 3.4
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
- Document content
- 3rd Party Assemblies
- Accessing document content
- Embedding Toolboxes
- AddinContent
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.
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;
}
}
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>
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.
- 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
- 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 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();
To deploy file content with your add-in:
- Set its Build Action = Content or Build Action = None (makes no difference which).
- 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.
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);
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:
- Set the .dll's Build Action = Content or Build Action = None (makes no difference which).
- 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.
ArcGIS toolboxes (.atbx), Legacy 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:
- Right-click and add a "New Folder" to your Visual Studio add-in project.
- Name the folder "Toolboxes".
-
Add any required subfolders to the Toolboxes folder. For example, subfolders for a 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.
-
Right-click on the Toolboxes folder, or any of its sub-folders, select Add->Existing Item.
-
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.
- Set the Build Action for your content files to "Content".
- Build your add-in project.
- 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.
- Deploy your add-in.
- Start pro to load your add-in.
- A Toolboxes folder is created at
\Users\<username>\AppData\Local\ESRI\ArcGISPro\Toolboxes
if not previously created. -
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.
- 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.
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