ProSnippets Framework - kataya/arcgis-pro-sdk GitHub Wiki
Language: C#
Subject: Framework
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: esri, http://www.esri.com
Date: 12/11/2020
ArcGIS Pro: 2.7
Visual Studio: 2017, 2019
.NET Target Framework: 4.8
How to subscribe and unsubscribe to events when the dockpane is visible or hidden
private SubscriptionToken _eventToken = null;
// Called when the visibility of the DockPane changes.
protected override void OnShow(bool isVisible)
{
if (isVisible && _eventToken == null) //Subscribe to event when dockpane is visible
{
_eventToken = MapSelectionChangedEvent.Subscribe(OnMapSelectionChangedEvent);
}
if (!isVisible && _eventToken != null) //Unsubscribe as the dockpane closes.
{
MapSelectionChangedEvent.Unsubscribe(_eventToken);
_eventToken = null;
}
}
//Event handler when the MapSelection event is triggered.
private void OnMapSelectionChangedEvent(MapSelectionChangedEventArgs obj)
{
MessageBox.Show("Selection has changed");
}
IPlugInWrapper wrapper = FrameworkApplication.GetPlugInWrapper("esri_editing_ShowAttributes");
var command = wrapper as ICommand; // tool and command(Button) supports this
if ((command != null) && command.CanExecute(null))
command.Execute(null);
// use SetCurrentToolAsync
FrameworkApplication.SetCurrentToolAsync("esri_mapping_selectByRectangleTool");
// or use ICommand.Execute
ICommand cmd = FrameworkApplication.GetPlugInWrapper("esri_mapping_selectByRectangleTool") as ICommand;
if ((cmd != null) && cmd.CanExecute(null))
cmd.Execute(null);
FrameworkApplication.ActivateTab("esri_mapping_insertTab");
// Define the condition in the DAML file based on the state
if (activate)
FrameworkApplication.State.Activate("someState");
else
FrameworkApplication.State.Deactivate("someState");
// The application is considered busy if a task is currently running on the main worker thread or any
// pane or dock pane reports that it is busy or intiializing.
// Many Pro styles (such as Esri_SimpleButton) ensure that a button is disabled when FrameworkApplication.IsBusy is true
// You would use this property to bind to the IsEnabled property of a control (such as a listbox) on a dockpane or pane in order
// to disable it from user interaction while the application is busy.
bool isbusy = FrameworkApplication.IsBusy;
System.Windows.Window window = FrameworkApplication.Current.MainWindow;
// center it
Rect rect = System.Windows.SystemParameters.WorkArea;
FrameworkApplication.Current.MainWindow.Left = rect.Left + (rect.Width - FrameworkApplication.Current.MainWindow.ActualWidth) / 2;
FrameworkApplication.Current.MainWindow.Top = rect.Top + (rect.Height - FrameworkApplication.Current.MainWindow.ActualHeight) / 2;
FrameworkApplication.Close();
//"GetEntryAssembly" should be ArcGISPro.exe
string version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString();
string _viewPaneID = "my pane"; //DAML ID of your pane
//You could have multiple instances (InstanceIDs) of your pane.
//So you can iterate through the Panes to get "your" panes only
IList<uint> myPaneInstanceIDs = new List<uint>();
foreach (Pane pane in FrameworkApplication.Panes)
{
if (pane.ContentID == _viewPaneID)
{
myPaneInstanceIDs.Add(pane.InstanceID); //InstanceID of your pane, could be multiple, so build the collection
}
}
foreach (var instanceID in myPaneInstanceIDs) //close each of "your" panes.
{
FrameworkApplication.Panes.ClosePane(instanceID);
}
var mapPanes = ProApp.Panes.OfType<IMapPane>();
foreach (Pane pane in mapPanes)
{
if (pane.Caption == "MyMap")
{
pane.Activate();
break;
}
}
var addin_infos = FrameworkApplication.GetAddInInfos();
StringBuilder sb = new StringBuilder();
foreach(var info in addin_infos)
{
if (info == null)
break;//no addins probed
sb.AppendLine($"Addin: {info.Name}");
sb.AppendLine($"Description {info.Description}");
sb.AppendLine($"ImagePath {info.ImagePath}");
sb.AppendLine($"Author {info.Author}");
sb.AppendLine($"Company {info.Company}");
sb.AppendLine($"Date {info.Date}");
sb.AppendLine($"Version {info.Version}");
sb.AppendLine($"FullPath {info.FullPath}");
sb.AppendLine($"DigitalSignature {info.DigitalSignature}");
sb.AppendLine($"IsCompatible {info.IsCompatible}");
sb.AppendLine($"IsDeleted {info.IsDeleted}");
sb.AppendLine($"TargetVersion {info.TargetVersion}");
sb.AppendLine($"ErrorMsg {info.ErrorMsg}");
sb.AppendLine($"ID {info.ID}");
sb.AppendLine("");
}
System.Diagnostics.Debug.WriteLine(sb.ToString());
MessageBox.Show(sb.ToString(), "Addin Infos");
// in order to find a dockpane you need to know it's DAML id
var pane = FrameworkApplication.DockPaneManager.Find("esri_core_ProjectDockPane");
// in order to find a dockpane you need to know it's DAML id
var pane = FrameworkApplication.DockPaneManager.Find("esri_core_ProjectDockPane");
// determine visibility
bool visible = pane.IsVisible;
// activate it
pane.Activate();
// determine dockpane state
DockPaneState state = pane.DockState;
// pin it
pane.Pin();
// hide it
pane.Hide();
// in order to find a dockpane you need to know it's DAML id
var pane = FrameworkApplication.DockPaneManager.Find("esri_core_contentsDockPane");
// get the undo stack
OperationManager manager = pane.OperationManager;
if (manager != null)
{
// undo an operation
if (manager.CanUndo)
await manager.UndoAsync();
// redo an operation
if (manager.CanRedo)
await manager.RedoAsync();
// clear the undo and redo stack of operations of a particular category
manager.ClearUndoCategory("Some category");
manager.ClearRedoCategory("Some category");
}
// in order to find a dockpane you need to know it's DAML id.
// Here is a DAML example with a dockpane defined. Once you have found the dockpane you can cast it
// to the dockpane viewModel which is defined by the className attribute.
//
//<dockPanes>
// <dockPane id="MySample_Dockpane" caption="Dockpane 1" className="Dockpane1ViewModel" dock="bottom" height="5">
// <content className="Dockpane1View" />
// </dockPane>
//</dockPanes>
Dockpane1ViewModel vm = FrameworkApplication.DockPaneManager.Find("MySample_Dockpane") as Dockpane1ViewModel;
//Opens the Backstage to the "About ArcGIS Pro" tab.
FrameworkApplication.OpenBackstage("esri_core_aboutTab");
//Gets the application's theme
var theme = FrameworkApplication.ApplicationTheme;
//ApplicationTheme enumeration
if (FrameworkApplication.ApplicationTheme == ApplicationTheme.Dark) {
//Dark theme
}
if (FrameworkApplication.ApplicationTheme == ApplicationTheme.HighContrast){
//High Contrast
}
if (FrameworkApplication.ApplicationTheme == ApplicationTheme.Default) {
//Light/Default theme
}
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Some Message", "Some title", MessageBoxButton.YesNo, MessageBoxImage.Information, MessageBoxResult.Yes);
Notification notification = new Notification();
notification.Title = FrameworkApplication.Title;
notification.Message = "Notification 1";
notification.ImageUrl = @"pack://application:,,,/ArcGIS.Desktop.Resources;component/Images/ToastLicensing32.png";
ArcGIS.Desktop.Framework.FrameworkApplication.AddNotification(notification);
private void ChangeCaptionImage()
{
IPlugInWrapper wrapper = FrameworkApplication.GetPlugInWrapper("MyAddin_MyCustomButton");
if (wrapper != null)
{
wrapper.Caption = "new caption";
// ensure that T-Rex16 and T-Rex32 are included in your add-in under the images folder and have
// BuildAction = Resource and Copy to OutputDirectory = Do not copy
wrapper.SmallImage = BuildImage("T-Rex16.png");
wrapper.LargeImage = BuildImage("T-Rex32.png");
}
}
private ImageSource BuildImage(string imageName)
{
return new BitmapImage(PackUriForResource(imageName));
}
private Uri PackUriForResource(string resourceName)
{
string asm = System.IO.Path.GetFileNameWithoutExtension(
System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
return new Uri(string.Format("pack://application:,,,/{0};component/Images/{1}", asm, resourceName), UriKind.Absolute);
}
//Pass in the daml id of your button. Or pass in any Pro button ID.
IPlugInWrapper wrapper = FrameworkApplication.GetPlugInWrapper("button_id_from daml");
var buttonTooltipHeading = wrapper.TooltipHeading;
private void SubscribeEvent()
{
ArcGIS.Desktop.Framework.Events.ActiveToolChangedEvent.Subscribe(OnActiveToolChanged);
}
private void UnSubscribeEvent()
{
ArcGIS.Desktop.Framework.Events.ActiveToolChangedEvent.Unsubscribe(OnActiveToolChanged);
}
private void OnActiveToolChanged(ArcGIS.Desktop.Framework.Events.ToolEventArgs args)
{
string prevTool = args.PreviousID;
string newTool = args.CurrentID;
}
public async Task Progressor_NonCancelable()
{
ArcGIS.Desktop.Framework.Threading.Tasks.ProgressorSource ps = new ArcGIS.Desktop.Framework.Threading.Tasks.ProgressorSource("Doing my thing...", false);
int numSecondsDelay = 5;
//If you run this in the DEBUGGER you will NOT see the dialog
await ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(() => Task.Delay(numSecondsDelay * 1000).Wait(), ps.Progressor);
}
public async Task Progressor_Cancelable()
{
ArcGIS.Desktop.Framework.Threading.Tasks.CancelableProgressorSource cps =
new ArcGIS.Desktop.Framework.Threading.Tasks.CancelableProgressorSource("Doing my thing - cancelable", "Canceled");
int numSecondsDelay = 5;
//If you run this in the DEBUGGER you will NOT see the dialog
//simulate doing some work which can be canceled
await ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(() =>
{
cps.Progressor.Max = (uint)numSecondsDelay;
//check every second
while (!cps.Progressor.CancellationToken.IsCancellationRequested) {
cps.Progressor.Value += 1;
cps.Progressor.Status = "Status " + cps.Progressor.Value;
cps.Progressor.Message = "Message " + cps.Progressor.Value;
if (System.Diagnostics.Debugger.IsAttached) {
System.Diagnostics.Debug.WriteLine(string.Format("RunCancelableProgress Loop{0}", cps.Progressor.Value));
}
//are we done?
if (cps.Progressor.Value == cps.Progressor.Max) break;
//block the CIM for a second
Task.Delay(1000).Wait();
}
System.Diagnostics.Debug.WriteLine(string.Format("RunCancelableProgress: Canceled {0}",
cps.Progressor.CancellationToken.IsCancellationRequested));
}, cps.Progressor);
}
//Set the tool's loadOnClick attribute to "false" in the config.daml.
//This will allow the tool to be created when Pro launches, so that the disabledText property can display customized text at startup.
//Remove the "condition" attribute from the tool. Use the OnUpdate method(below) to set the enable\disable state of the tool.
//Add the OnUpdate method to the tool.
//Note: since OnUpdate is called very frequently, you should avoid lengthy operations in this method
//as this would reduce the responsiveness of the application user interface.
internal class SnippetButton : ArcGIS.Desktop.Framework.Contracts.Button
{
protected override void OnUpdate()
{
bool enableSate = true; //TODO: Code your enabled state
bool criteria = true; //TODO: Evaluate criteria for disabledText
if (enableSate)
{
this.Enabled = true; //tool is enabled
}
else
{
this.Enabled = false; //tool is disabled
//customize your disabledText here
if (criteria)
this.DisabledTooltip = "Missing criteria 1";
}
}
}
public static void ExampleUsage() {
//Image 'Dino32.png' is added as Build Action: Resource, 'Do not copy'
var img = ForImage("Dino32.png");
//Use the image...
}
public static BitmapImage ForImage(string imageName) {
return new BitmapImage(PackUriForResource(imageName));
}
public static Uri PackUriForResource(string resourceName, string folderName = "Images") {
string asm = System.IO.Path.GetFileNameWithoutExtension(
System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
string uriString = folderName.Length > 0
? string.Format("pack://application:,,,/{0};component/{1}/{2}", asm, folderName, resourceName)
: string.Format("pack://application:,,,/{0};component/{1}", asm, resourceName);
return new Uri(uriString, UriKind.Absolute);
}
// There are two ways to prevent ArcGIS Pro from closing
// 1. Override the CanUnload method on your add-in's module and return false.
// 2. Subscribe to the ApplicationClosing event and cancel the event when you receive it
internal class Module1 : Module
{
// Called by Framework when ArcGIS Pro is closing
protected override bool CanUnload()
{
//return false to ~cancel~ Application close
return false;
}
internal class Module2 : Module
{
public Module2()
{
ArcGIS.Desktop.Framework.Events.ApplicationClosingEvent.Subscribe(OnApplicationClosing);
}
~Module2()
{
ArcGIS.Desktop.Framework.Events.ApplicationClosingEvent.Unsubscribe(OnApplicationClosing);
}
private Task OnApplicationClosing(System.ComponentModel.CancelEventArgs args)
{
args.Cancel = true;
return Task.FromResult(0);
}
#region How to determine when a project is opened
protected override bool Initialize() //Called when the Module is initialized.
{
ProjectOpenedEvent.Subscribe(OnProjectOpened); //subscribe to Project opened event
return base.Initialize();
}
private void OnProjectOpened(ProjectEventArgs obj) //Project Opened event handler
{
MessageBox.Show($"{Project.Current} has opened"); //show your message box
}
protected override void Uninitialize() //unsubscribe to the project opened event
{
ProjectOpenedEvent.Unsubscribe(OnProjectOpened); //unsubscribe
return;
}
public ProSnippetMapTool()
{
//Set the MapTool base class' OverlayControlID to the DAML id of your embeddable control in the constructor
this.OverlayControlID = "ProAppModule1_EmbeddableControl1";
}
protected override void OnToolMouseDown(MapViewMouseButtonEventArgs e)
{
if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
e.Handled = true;
}
protected override Task HandleMouseDownAsync(MapViewMouseButtonEventArgs e)
{
return QueuedTask.Run(() =>
{
//assign the screen coordinate clicked point to the MapTool base class' OverlayControlLocation property.
this.OverlayControlPositionRatio = e.ClientPoint;
});
}
C:\>"C:\Program Files\ArcGIS Pro\bin\ArcGISPro.exe"
If your Add-in requires the use of custom command line arguments your arguments must be of the form "/argument" - note the forward slash "/". There must be no white space. If the command line contains a project to be opened (see Open project from command line) then custom arguments or switches must be placed before the project filename argument.
string[] args = System.Environment.GetCommandLineArgs();
foreach (var arg in args)
{
// look for your command line switches
}
Application accelerators can be added to your Add-in config.daml using an accelerators/insertAccelerator DAML element with the refID of the element to which you are associating the accelerator (i.e. short cut).
<accelerators>
<insertAccelerator refID="esri_core_openProjectButton" flags="Ctrl" key="O" />
<insertAccelerator refID="esri_core_redoButton" flags="Ctrl" key="Y" />
<insertAccelerator refID="esri_core_undoButton" flags="Ctrl" key="Z" />
</accelerators>
Note: Use the deleteAccelerator and updateAccelerator DAML elements within an updateModule element to remove or alter application accelerators respectively. Flags can be one of: Shift, Ctrl, Alt, Ctrl+Shift, Alt+Shift, Ctrl+Alt, Ctrl+Alt+Shift
There are many ArcGIS Pro styles defined which can be applied to buttons, labels and other controls on your panes and dockpanes to make your add-ins look and feel seamless with ArcGIS Pro. Some of the most common styles are listed below. For more styles and colors see the Styling-With-ArcGIS-Pro sample in the Community Samples repo.
Button styles
<Button Content="Button" Style="{StaticResource Esri_SimpleButton}" ToolTip="Button">
<Button Content="Button" Style="{StaticResource Esri_BackButton}" ToolTip="Button">
<Button Content="Button" Style="{StaticResource Esri_BackButtonSmall}" ToolTip="Button">
<Button Content="Button" Style="{StaticResource Esri_CloseButton}" ToolTip="Button">
Dockpane heading style
<TextBlock Text="MyDockPane" Style="{StaticResource DockPaneHeading}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>