ProConcepts 3.0 Migration Guide - Esri/arcgis-pro-sdk GitHub Wiki

ArcGISPro 3.0 is a breaking change release. The 3.0 API breaking changes and migration procedure for 2.x add-ins, configurations, core host applications, and plugin datasource extensions are discussed within this document. The migration procedures in this document apply to migrating a 2.x addin to any 3.x release.

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

In this topic

Overview

At 3.0, ArcGIS Pro moved to .NET 6, Microsoft's latest version of .NET (formerly known as ".NET Core") with Long Term Support, LTS. This is a breaking release change meaning that:

  • Addins, configurations, plugin datasources, and core host applications compiled on version 2.x of Pro will not work with Pro 3.0 or better
  • The public APIs (of Pro) are subject to change
  • The major version increment of Pro is changing from 2.x to 3.x.

Note: At ArcGIS Pro 3.3, Pro moved to .NET 8 from .NET 6. If you are migrating from Pro 2.x at ArcGIS Pro 3.3+, then, generally speaking, substitute .NET 8 for occurrences of .NET 6 within the procedures described in this document.

Pro Document Version Change

At ArcGIS Pro 3.0, the document version of ArcGIS Pro Projects, project templates, packages, layer and map files (.aprx, .ppkx, .aptx, .pagx, .lyrx, .mapx, etc.) was also changed to 3.0. Any project, template, package, etc. created or saved using ArcGIS Pro 3.0 will be saved with a document version of 3.0. This includes a file, package, template, etc. with a document version of 2.x opened in 3.0. Consult the ArcGIS Pro migration help topic in the Pro help for more detailed information.

Note: Map, Layout, and Layer (.mapx, .pagx, .lyrx) and packages (.mpkx, .lpkx), created at 2.x, can be read by public API at 3.0 with no changes. This is covered in the Map, Layout, and Layer Files and Packages section of this document.

Forwards Compatibility

Addins, configurations, and plugin datasources are forwards compatible across minor releases of Pro only. They are not forwards compatible across major releases. As the move from 2.9 to 3.0 is a major release, addins, configurations, plugin datasources, and core host applications compiled against a 2.x version of Pro must be migrated to Pro 3.x and recompiled.

The migration procedure is detailed below in the Migrating to Pro 3.0 section.

Registering addins

Attempting to register a 2.x addin, configuration, or plugin datasource against Pro 3.x will fail though it will still be deployed. RegisterAddin.exe will pop-up a warning message alerting you that the add-in or configuration you are attempting to install won't be loaded because it is incompatible with the current version of Pro.

reg-addin-failed.png

Proceeding with the installation deploys the archive file to the respective folder but when Pro starts it will not be loaded.

Add-in and Configuration Does Not Load

In many cases you may already have add-ins or configurations installed on your machine from a 2.x release when you upgrade Pro to 3.x. When you run Pro, previously installed addins, configurations, and plugin datasources from a 2.x release will not load in Pro 3.x. Any add-in that is incompatible with the current release will be shown as disabled on the Add-in Manager backstage tab of Pro. The add-ins must be migrated to Pro 3.0 or better before they can be loaded. The same is true for configurations, plugin datasources, and core host applications*.

addin-did-not-load.png

*Core host applications must be migrated but they are not loaded by, nor registered with, Pro.

Build Server use of ArcGISSignAddIn.exe

Note: per ProConcepts Advanced Topics, manual configuration of a build server, configuring a build server to use ArcGISSignAddIn.exe at 2.x (without installing ArcGIS Pro) required that the DADFLib.dll and zlibwap.dlls also be available in the server's Pro bin folder being referenced (for compilation purposes).

At 3.x, to sign the add-in using ArcGISSignAddIn.exe, now copy ArcGISSignAddIn.exe, ArcGISSignAddIn.dll, ArcGISSignAddIn.runtimeconfig.json and DADFLib.dll to your server's Pro bin folder. Refer to the previously mentioned ProConcepts Advanced Topics for more information.

Migrating to Pro 3.0

Any 2.x Addins, configurations, plugin datasources, and core host application must be migrated to Pro 3.x before they can be run in (or on) the Pro application. The process of migration is the main subject of this document and generally follows a two-step process:

  1. Addins, configurations, plugin datasources, and core host applications must be converted from .NET Framework 4.x to .NET 6
  2. Addins, configurations, plugin datasources, and core host applications must be recompiled against 3.x and resulting compilation errors (from API breaking changes) must be fixed.

For the remainder of this document the 4 extensibility patterns of Pro - Addins, configurations, plugin datasources, and core host applications - will be collectively referred to as "addins" unless there is a particular nuance or idiosyncracy of one of the extensibility patterns that requires it be specifically mentioned for particular attention.

Install Pro and the Pro SDK for 3.x

To migrate your addins at 3.0 or better you must install the 3.x Pro SDK. Minimum requirements for the Pro SDK at 3.x are Visual Studio 2022 and .NET 6. Moving to ArcGIS Pro 3.x SDK from 2.x is not an upgrade. To install ArcGIS Pro 3.x SDK when coming from 2.x, you must use the Extensions > Manage Extensions > Online and search for the Pro SDK vsixs from the Visual Studio marketplace. Use the search string "ArcGIS Pro SDK". There will be 3 separate extensions you can install:

  • ArcGIS Pro SDK for .NET,
  • ArcGIS Pro SDK for .NET (Utilities)
  • ArcGIS Pro SDK for .NET (Migration).

Some things to keep in mind: ArcGIS Pro 3.x SDKs require Visual Studio 2022. Make sure you search for the ArcGIS Pro SDK inside Visual Studio 2022 or you will find ArcGIS Pro 2.9 SDK. Also ensure you have .NET 6.0.5 installed or better. There will not be an update notification from Visual Studio even if you have a previous version of the SDK installed and "Automatic updates" turned on.

Step 1 Conversion

Migration to Pro 3.x begins by converting your Addins from the .NET Framework to .NET 6. It is perfectly acceptable to migrate your addin to .NET 6 by hand. In this case, simply use the Pro SDK Templates to create a new addin (or configuration, or plugin, or corehost) project and copy over relevant source files, 3rd party dependencies (updated as needed to support .NET), Nuget references, etc. to re-construct a 3.x version of your 2.x project. This may be desirable if your addin has particular idiosyncracies or special settings that require manual reconfiguring for .NET. Otherwise, use the "Pro Migrate Project" utility which is the focus of the rest of this particular section.

Note: addin developers should make whatever backups or copies of the pre-converted project as they require before running the conversion utility. Run Pro Fix References (from the proapp-sdk-utilities.vsix) to update the assembly paths within your 2.x .csproj/.vbproj before running the migration tool if the addin assembly paths need to be updated (e.g. you copied the 2.x addin from another machine).

To install "Pro Migrate Project", run the "proapp-sdk-migration.vsix" that comes with the Pro SDK at 3.0 and 3.1. This will add the utility to the Visual Studio 2022 project context menu (alongside the other Pro SDK context menu options).

MigrateUtility.png

To run the migration utility, open the respective addin, configuration, plugin datasource, or core host application project in Visual Studio 2022. Next, right-click and run the "Pro Migrate Project" option to convert the addin project (whether addin, configuration, plugin datasource, or core host application) to .NET 6. The conversion utility can be used to convert both C# and VB.Net 2.x addin projects.

When the "Pro Migrate Project" conversion utility is run it prompts the user as to whether or not they want to proceed:

MigrateUtility_YesNo.png

Assuming "Yes" is selected, the conversion utility performs the relevant conversion of the addin project to .NET 6. The user should acknowledge the "migration completed successfully" prompt and click "Reload" on the Visual Studio prompt to reload the project. Post conversion:

  • The target framework will have been changed to .NET 6.0
  • The output addin .csproj or .vbproj will be converted to .NET project format (which is significantly different from the .NET Framework format)
    • The project References folder will have been changed to a .NET Dependencies folder
  • The desktopVersion attribute in the Config.daml will have been changed to 3.x
  • For plugins, the version attribute will have been changed to 3.x
  • For plugins, the language attribute will have been changed to CLR:PluginDS.
  • Build Action "AddinContent" will have been changed to Build Action "Content" (refer to AddinContent for more information)
  • Custom Targets are copied over unchanged except for <Target Name="AfterBuild" ...>. It will be changed to <Target Name="SignAddin" ...>.
  • Pro SDK Nuget references are updated to the reference the 3.x Nuget. Refer to ProGuide ArcGIS Pro Extensions Nuget for more information on the 3.x Pro SDK Nuget.
  • Project xaml resource dictionaries will have been changed from Build Action "Resource" to Build Action "Page"
  • External links will remain as external links
  • 3rd party DLL references are not converted. They must be added back by hand, as needed.*

At the conclusion of the migration, a migration status report (in both an html and txt format) will have been added to the Visual Studio project TOC. Review the migration status report for any outstanding conversion issues the migrate tool identified that need to be resolved by hand. For example, updating the Pro SDK Nuget reference to 3.x.

*Any Addins making use of 3rd party Nuget packages or particular custom or non-Microsoft .NET Framework libraries and dlls in their 2.x Visual Studio projects will have to make whatever changes may be needed to reference appropriate .NET 6 equivalents by hand.

Before and After conversion view of the Visual Studio TOC of a 2.x Addin:

addin-before-after2.png

Conversion Notes

Additional notes concerning addin conversion.

Target Framework

The target framework of addin .csproj's and .vbproj's must be changed to .NET 6 from which ever version of .NET Framework it is currently using at 2.x (most likely .NET Framework version 4.8). The Pro Migrate Project utility will automatically change the target framework for you, otherwise it will need to be changed by hand*.

*There are structural differences between a Visual Studio .NET Framework project and a Visual Studio .NET (or ".NET Core") project. It may be that, if you choose not to use the Pro Migrate Project utility, you have to make a new Visual Studio 2022 addin project with the .NET 6 target framework set and then copy the relevant 2.x addin content "over" manually.

Config.daml desktopVersion and Config.xml version Attributes

Addins and configurations created prior to 3.0 will have a desktopVersion value in their Config.daml of 2.9 or less. Plugin datasources created prior to 3.0 will have a version attribute on the <Target ...> element in their Config.xml of 2.9 or less. The conversion utility will change the Config.daml desktopVersion and Config.xml version (for plugin datasources) to 3.x, otherwise these changes must be made by hand.

Refer to Pro Concepts Advanced Topics, Add-in Versioning for more information on versioning.

Additionally, if you are using the Pro Migrate Project utility to migrate a 2.x addin to 3.2, the dekstopVersion attribute in your config.daml may be being set incorrectly. At 3.2, the migration tool may be writing the build number into the dekstopVersion attribute twice. For example, in the snippet below notice that the build number in the desktopVersion attribute has been duplicated - 3.2.0.49743.49743 whereas it should be 3.2.0.49743:

<AddInInfo id="{xxxx-xxx}" version="1.0" desktopVersion="3.2.0.49743.49743"

To fix this issue, simply remove the duplicate value of the build number in the desktopVersion attribute. The snippet below shows the correct format for the desktopVersion attribute (the duplicate build number has been removed):

<AddInInfo id="{xxxx-xxx}" version="1.0" desktopVersion="3.2.0.49743"

Note: this issue has only been observed with the Pro Migrate Project utility at 3.2. This issue has not been observed at 3.0 or 3.1.

Config.xml Language Attribute

At 3.0 or better, the 2.x <AddIn language="CLR4.X.X" ...> attribute in the Config.xml must be changed to CLR:PluginDS. The conversion utility will make this change automatically to the Config.xml, otherwise the change must be made by hand.

Config.xml
<!-- before at 2.x -->
<AddIn language="CLR4.7.2" library="AcmePluginDatasource.dll" namespace="AcmePluginDatasource">
    <ArcGISPro>
      ...

<!-- at 3.x  -->
<AddIn language="CLR:PluginDS" library="AcmePluginDatasource.dll" namespace="AcmePluginDatasource">
    <ArcGISPro>
      ...

AddinContent

At 3.0 and better, the custom Visual Studio build action AddinContent is no longer supported. To embed custom content within the addin archive, addins should use the built-in build action Content instead.

addin-content2.png

The Pro SDK "Pro Migrate Project" tool will automatically convert AddinContent types to Content in the converted .csproj and .vbproj, otherwise the change must be made by hand.

<!-- At 2.x -->
 <ItemGroup>
   <AddInContent Include="ReadMe.md" />
 </ItemGroup>

<!-- At 3.x -->
<ItemGroup>
  <Content Include="ReadMe.md" />
</ItemGroup>

Target "AfterBuild"

If your addin or configuration is using a <Target Name="AfterBuild" ...> element within your .csproj or .vbproj (i.e. as documented in the ProGuide Digitally Signed Addins and Configrations for 2.x) then that Target element will be changed from: <Target Name="AfterBuild" DependsOnTargets="PackageArcGISContents"> to <Target Name="SignAddIn" AfterTargets="PackageArcGISContents">. This change is likewise reflected in the previously mentioned ProGuide for 3.x.

All other <Target ...> elements are copied over unchanged. The contents of all Target elements, to include the above mentioned <Target Name="AfterBuild" ...>, are also copied over unchanged (eg any <Exec ...> elements). These may or may not need to be modified by hand depending on the idiosyncracies of the Target content itself relevant to .NET.

For more information on the use of a Target element with MSBuild consult Target Element - MSBuild

Other Custom Content

Any custom .csproj or .vbproj content added as Resource, Content, AddinContent (see above), or None will be migrated into the converted .csproj or .vbproj by the migration tool. The "Copy to Output Directory" setting is also preserved*.

converted-content.png

* There is a bug with Visual Studio version 17.0.0 - 17.1.2. Copy to Output Directory always shows "None" in the item properties window. To determine if the Copy to Output Directory is properly set, view the item definition in the .csproj or .vbproj file. It should look similar to the below xml:

<!-- content type None, CopyToOutputDirectory 'Copy Always' -->
<ItemGroup>
    <None Include="Content\MS_Word_Doc.docx">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>

<!-- content type None, CopyToOutputDirectory 'Copy if Newer' -->
<ItemGroup>
    <None Include="Content\MS_Word_Doc.docx">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>

XAML Resources Marked As Page

With Visual Studio 2019 | 2017 and .NET Framework projects, xaml resources (i.e. 'Resource Dictionaries') are added to a project with a build action of type "Resource". For Visual Studio 2022 and .NET 6, xaml resources must be marked as build action "Page". This includes xaml templates for DAML galleries and combo boxes. Xaml templates still marked as "Resource" in a .NET 6 project will not load and the pack Uri will return null.

When migrating from 2x to 3.x, the migration tool will make the xaml resource build action change for you automatically, however, if converting your addins by hand, ensure that your xaml resources are changed to build action "Page".

resource-page.png

When adding a new Resource Dictionary at 3.x via the Visual Studio 2022 item template, Visual Studio will automatically set the correct build action for you so new xaml resource references do not have to be changed.

3rd Party References and Non-Esri DLLs

The migration tool cannot discern if a given DLL reference is compatible with .NET or not. Therefore all 3rd party references (i.e. non-Esri) are excluded from the converted .csproj/.vbproj. The addin developer should add the relevant references back to the converted project by hand. Developers may also have to similarily add back in Nuget package references for the same reason.

NotUsed Files

In .NET 6 and Visual Studio 2022, all source files in the project folder are automatically included in the build by default. In .NET Framework Visual Studio 2017/2019 projects, source files that are present in the project folder, but are not explicitly referenced in the 2.9 project file, are ignored by the build or "not used". Therefore, if the migration tool finds source files in the project folder that are "not used", it will add a ".NotUsed" extension to ensure they remain excluded from the .NET 6/3.x build. Refer to MSBuild EnableDefaultItems for more information on enabling or disabling default content.

Copy Local Not Working

Copy Local is not working correctly with Microsoft Visual Studio 2022 versions 17.1.6 or earlier. In ArcGIS Pro, we use a setting of "CopyLocal=No" to prevent ArcGIS Pro assemblies being written out into the adding archive. Essentially, we do not want the Pro assembly references to be written out to the addin assembly cache at runtime. Instead, we want the addin to use the Pro assemblies that the Pro application has already loaded (from the Pro bin folder). Additionally, "CopyLocal=No" drastically reduces the size of the addin archive as the assemblies are not included in it.

To fix this issue, it is recommended that you upgrade your Visual Studio 2022 to 17.2 or better. After upgrading your Visual Studio 2022, “clean” the addin project and ensure that the “obj” folder, within the project, is deleted. Check all Pro assembly reference properties in the .csproj or .vbproj and ensure that CopyLocal is set to No. Rebuild the project.

If you remain on 17.1.6 or less, the migration tool will "fix" the copy local behavior for assembly references already in the addin project being migrated. However, if you do add additional assembly references - including non-Esri references - to the project after running the migration, and CopyLocal=No, then they must be fixed manually (unless you upgrade your Visual Studio 2022 to 17.1.2.

To fix an assembly reference with CopyLocal=No, edit the .csproj/.vbproj entry for the relevant assembly|assemblies and add a <Private>False</Private> to the entry to enforce the CopyLocal=No behavior (note: CopyLocal Yes\No is stored as True|False in the project file). <Private> is the proper documented tag in MSBuild to use to control Copy Local. A <CopyLocal> tag, if present, will be ignored.

Here are some before and after examples of Assembly References without and then with the fix:

This is the default - no Copy Local specified so the behavior will default to true or "Yes":

<Reference Include="ArcGIS.Desktop.Framework">
  <HintPathC:\Program Files\ArcGIS\Pro\bin\ArcGIS.Desktop.Framework.dll</HintPath>
</Reference>

In this example, the user, via the VS 2022 UI has set "CopyLocal=No" on the UI - note that VS 2022 has added a <CopyLocal>False</CopyLocal> tag to the assembly reference. This will correctly sync the Visual Studio UI but, unfortunately, will be ignored by MSBuild and the assembly will be copied local anyway. This is the Visual Studio bug - it adds a "CopyLocal" tag instead of a "Private" tag. It should be using a "Private" tag.

<Reference Include="ArcGIS.Desktop.Framework">
  <HintPathC:\Program Files\ArcGIS\Pro\bin\ArcGIS.Desktop.Framework.dll</HintPath>
  <CopyLocal>False</CopyLocal>  <!-- this tag is -ignored- -->
</Reference>

The reference must be fixed by hand to include the missing <Private>False</Private> tag to disable copy local. "" is the documented tag in MSBuild for controlling copy local behavior:

<Reference Include="ArcGIS.Desktop.Framework">
  <HintPathC:\Program Files\ArcGIS\Pro\bin\ArcGIS.Desktop.Framework.dll</HintPath>
  <CopyLocal>False</CopyLocal>  <!-- this tag is -ignored- -->
  <Private>False</Private> <!-- to prevent copy local, this tag is -required- -->
</Reference>

Note that the "CopyLocal" tag can also be deleted if you prefer - though the UI CopyLocal selection will show blank (no option selected).

<Reference Include="ArcGIS.Desktop.Framework">
  <HintPathC:\Program Files\ArcGIS\Pro\bin\ArcGIS.Desktop.Framework.dll</HintPath> 
  <!-- to prevent copy local, this tag is -required- -->
  <Private>False</Private> <!-- note: CopyLocal tag has been deleted -->
</Reference>

Step 2 Recompile and Fix API Breaking Changes

Once the addin has been converted, the second step is to compile the addin and fix all compiler errors resulting from the conversion to .NET 6. Breaking changes will generally fall into one of 4 categories:

  1. Changes related to the switch from .NET Framework 4.x to .NET 6 (minor). You may need to add the Microsoft Windows Compatibility Pack nuget to your project.
  2. Classes, methods, and properties previously deprecated at a 2.x release are deleted at 3.0
  3. Object IDs are now defined as 64 bit (long) in the Pro APIs at 3.0. This "extra width" in the public APIs is to accommodate any present and future changes to 64 bit oids in the geodatabase and underlying databases.
  4. API Changes related to naming, parameter ordering, etc. to improve consistency, quality, and remove duplicate functionality. This also includes breaking changes related to removal of CIM XML persistence at 3.0.

To fix a compiler error, the old reference (to class, method, property, enum, etc.) must be changed to the new reference. An overview of each of these categories follows.

Note: There may be other changes required, not strictly API (breaking) changes, related to porting custom addin content such as a python toolbox or custom project and application properties for use with 3.x. These considerations are covered in the Additional Migration Notes section of this document.

.NET 6

Breaking changes related to the switch to .NET 6 are relatively minor however there are still some breaking changes that may affect you. For example, 3rd party User Controls or 3rd party libraries that use an API or technology that isn't available in .NET will need to be switched to a .NET equivalent control or library. APIs that were not ported, by Microsoft, to .NET because they relied on Windows-specific technology include the Windows registry and GDI+ (eg System.Drawing). For a complete list, reference the Microsoft Breaking changes in .NET 6.

Microsoft Windows Compatibility Pack

Microsoft provides the Windows Compatibility Pack Nuget that can be added to your projects to automatically address many of the .NET Framework to .NET breaking change issues. You can read more about the Windows Compatibility Pack here and Microsoft's own migration, or "porting" guide for .NET Framework to .NET here. The Pro SDK samples and snippets at 3.0 and 3.1 also make use of the Windows Compatibility Pack Nuget. Two of the more common Windows APIs that may be in use within your addins that will require the Compatibility Pack are System.Drawing and Registry access.

Deprecated Types and Members

Deprecated types and members were deleted at 3.0. Use of a deprecated type or member will have shown up as a compiler warning previously in Visual Studio. Code that referenced a deprecated type or member must be changed to use its replacement. All types and members previously marked as [Obsolete] have been deleted. Addins that were referencing deprecated content at 2.x usually get a compiler warning if the classes, methods, or property were marked with the Obsolete attribute in the code base.

Breaking changes in the public API are covered in much more detail in the Breaking Changes By Assembly section.

64-bit Object IDs

To accommodate current and future expansion of the Geodatabase to consume 64-bit object ids, or "oids", the public API has switched "oid" parameter references and return values from Int32 to long. This also includes row and feature counts returned from methods like Selection.GetCount() topic7637.html. This should be a fairly mechanical change for addin code impacted with this change. For addins that favor the use of var to implicitly declare variable type there should be no noticeable code change as the compiler will automatically change the implicit type of "var" from int to long. Otherwise, for cases where an oid or "count" variable is explicitly declared as int, addins should make the appropriate change to declare the relevant variables as long. Attempting to assign a long into an int value will result in a compiler error similar to Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?). Addins storing oid values in proprietary and/or custom (non-esri) data stores may also need to change the relevant schemas to accommodate long, rather than int values.

Plugin Datasources
Plugin datasource custom table template classes must derive from ArcGIS.Core.Data.PluginDatastore.PluginTableTemplate. At 3.0 and better, PluginTableTemplate also consumes 64-bit object ids. Changes should be made therefore, as needed, to plugin datasource custom code to accommodate 64 bit oids. Specifically, PluginTableTemplate.GetNativeRowCount will return long and the oid values returned in the PluginRow Values collection from a Search must also be 64 bit (i.e. long). Object ids passed in to the Search method, via the query filter ObjectIDs property, will also be of type long.

//At 2.x
 public override int GetNativeRowCount() {  
      //your implementation here...
      ...
 }

//3.x - note the return type
public override long GetNativeRowCount() {  
      //your implementation here...
      ...
 }

Additional API Breaking Changes

Additional API breaking changes (i.e. breaking changes other than those related to .Net 6, deprecated content, and 64 bit oids) include:

  • Name changes or "upper-casing/lower-casing" changes to improve the consistency of the API.
  • Consolidation of many of the overloads and functions created over the course of 2.x releases. The same is also true for Events.
  • Changes to parameters and return values (e.g. from array to IEnumerable or list)
  • In some cases, classes, methods, properties, etc. have been replaced, rather than renamed, to acomodate additional or improved capabilities at 3.0.
  • In some cases, classes have changed namespaces to improve consistency.

The main breaking changes are covered in the following Breaking Changes By Assembly section.

There are also changes related to the ArcGIS Pro CIM persistence model changing from xml at 2.x to json at 3.0 or better. Breaking changes related to CIM persistence usage are covered in the CIM Persistence section.

Additional Resources

Beyond the information provided in this migration guide, addin developers may find it useful to consult the Pro SDK Community Samples as well as the Pro Snippets all of which have been converted to 3.x.

The What's New for Developers at 3.0 in the API reference contains the complete listing of all API changes that occurred at 3.0.

Breaking Changes By Assembly

This section provides an overview of the main breaking changes, per assembly, to assist you in 3.x addin migration. Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Core.dll

CIM

At 3.0, addins should use json as the persistence format. To convert serialized CIM xml previously persisted at 2.x to a 3.x CIM object instance, addins should use ArcGIS.Core.CIM.XmlUtils.UpgradeAndDeserializeCIMObject(xml), topic 75062 which was added at 3.0.

Breaking changes related to CIM persistence usage are covered in detail in the CIM Persistence section. However, a basic example follows:

 //At 2.x - persistence using xml - cimObject.ToXml()
 simpleRenderer.ToXml()
 //and the derived static CIMObject.FromXml() method
 //eg for CIMSimpleRenderer
 var simpleRenderer = CIMSimpleRenderer.FromXml(xml_string);

 //At 3.x
 //addins should use cimObject.ToJson()
 simpleRenderer.ToJson();
 //and the derived static CIMObject.FromJson() method
 //eg for CIMSimpleRenderer
 var simpleRenderer = CIMSimpleRenderer.FromJson(json_string);

//use to convert CIM xml previously persisted at 2.x -consult the 
//CIM Persistence section for more details
 var simpleRenderer = (CIMSimpleRenderer)ArcGIS.Core.CIM.XmlUtils.UpgradeAndDeserializeCIMObject(
                                                               simple_renderer_xml_from_2x);

At 3.0, there were changes to the XML persistence model in use with CIMGenericView and implementations of map pane impersonation using custom map pane view models that derive from TOCMapPaneProviderPane, topic 16597. Custom map panes and impersonation map panes are peristed as CIMGenericView, topic 1481, in the .aprx. Consult the Custom CIMGenericView and ViewXML section of this document for the relevant changes.

At 3.0, ArcGIS.Core.CIM.CIMObjectMarker3D was removed. Addins should use ArcGIS.Core.CIM.CIMMglTFMarker3D instead. The GL Transmission Format (.glTF file) is an industry-standard interchange format for the transport of 3D models. You can find more information on .glTF here. Additionally, if you were using the CIMObjectMarker3D.ExportWeb3DObjectResource() method to export 3D symbols to json format for use with the 3D Esri Javascript format then you should switch to creating web styles (for those markers) instead. More information on publishing 3D symbols with web styles can be found in the online documentation: Share a web style. This blog post may also be useful: How To Publish Web Styles with 3D Symbols.

Data / Geodatabase

To accommodate current and future expansion of the Geodatabase to consume 64-bit object ids, or "oids", the public API has switched "oid" parameter references and return values from Int32 to long. This also includes row and feature counts which are now returned as long also. This was covered in the 64-bit Object IDs section earlier within this document.

At 3.0, the Reconcile and Post API was enhanced. A Post can now be performed as a separate action from Reconcile in the public API. This affects code that previously used the ReconcileDescription class and version.Reconcile() method. Consult ProConcepts Geodatabase for more information.

 //At 2.x - 
 ReconcileDescription reconcileDescription = new ReconcileDescription(parentVersion);
 reconcileDescription.ConflictResolutionMethod = ConflictResolutionMethod.Continue; //continue if 
 reconcileDescription.WithPost = true;                                              //conflicts are found

 // Reconcile and post
 ReconcileResult reconcileResult = currentVersion.Reconcile(reconcileDescription);
 ReconcileResult.HasConflicts can be checked as-needed


 //At 3.x use ReconcileOptions
 var reconcileOptions = new ReconcileOptions(parentVersion);
 reconcileOptions.ConflictResolutionMethod = ConflictResolutionMethod.Continue; //continue if 
                                                                                //conflicts are found
 reconcileOptions.ConflictDetectionType = ConflictDetectionType.ByRow; //Default
 reconcileOptions.ConflictResolutionType = ConflictResolutionType.FavorTargetVersion;//or FavorEditVersion

 // Reconcile and post as two separate actions
 ReconcileResult reconcileResult = currentVersion.Reconcile(reconcileOptions);
 if (!reconcileResult.HasConflicts) {
   //No conflicts, perform the post
   var postOptions = new PostOptions(parentVersion);
   //var postOptions = new PostOptions(); for default version
   postOptions.ServiceSynchronizationType = ServiceSynchronizationType.Synchronous;//Default
   currentVersion.Post(postOptions);
 }

 //Reconcile and post as a single action (similar to 2.x)
 ReconcileResult reconcileResult = currentVersion.Reconcile(reconcileOptions, postOptions);
 if (reconcileResult.HasConflicts) {
   //TODO resolve conflicts
 }

All geodatabase exception classes deriving from ArcGIS.Core.Data.GeodatabaseException and including ArcGIS.Core.Data.GeodatabaseException have been moved to a new namespace: ArcGIS.Core.Data.Exceptions for consistency.

 //At 2.x
 using ArcGIS.Core.Data;
 ...
 try {
   ...
 } catch(GeodatabaseException ge) {

 //At 3.x
 using ArcGIS.Core.Data;
 using ArcGIS.Core.Data.Exceptions

 try {
   ...
 } catch(GeodatabaseException ge) {//or use explicit reference 
                                   //ArcGIS.Core.Data.Exceptions.GeodatabaseException 

Utility Network

The namespace ArcGIS.Core.Data.UtilityNetwork.NetworkDiagrams has been replaced with ArcGIS.Core.Data.NetworkDiagrams.

Geometry

The geometry and derived geometry classes ToXML() method has been renamed to ToXml() (note the change in case). Addins referencing ToXML() will need to change their code to reference ToXml() instead. Additionally, SpatialReferenceBuilder.FromXML() has been renamed to SpatialReferenceBuilder.FromXml().

 //At 2.x
 var xml = geometry.ToXML();
 var xml = envelope.ToXML();
 var xml = mapPoint.ToXML();
 var xml = polygon.ToXML();
 var xml = polyline.ToXML();

//At 3.x
 var xml = geometry.ToXml();
 var xml = envelope.ToXml();
 var xml = mapPoint.ToXml();
 var xml = polygon.ToXml();
 var xml = polyline.ToXml();

ArcGIS.Core.Geometry.GeometryException and derived geometry exceptions have been moved to a new namespace ArcGIS.Core.Geometry.Exceptions. Please change references to existing geometry exceptions accordingly.

 // At 2.x
 using ArcGIS.Core.Geometry;
 ...
 try {f
   ...
 } catch(GeometryException ge) {

 // At 3.x
 using ArcGIS.Core.Geometry;
 using ArcGIS.Core.Geometry.Exceptions

 try {
   ...
 } catch(GeometryException ge) {//or use explicit reference 
                                //ArcGIS.Core.Data.Exceptions.GeometryException 

At 3.x, geometry enums with the prefix esri have been changed to have the prefix Esri or have had the esri prefix eliminated. This also includes the enum values.

enum esriArcOrientation --> enum ArcOrientation  also, `esri` value prefix has been removed
enum esriClothoidCreateMethod -->enum ClothoidCreateMethod
enum esriCurveDensifyMethod -->enum CurveDensifyMethod
enum esriPatchType -->enum PatchType
enum esriTextureCompressionType -->enum TextureCompressionType

enum EsriShapeExportFlags --> all value prefixes changed from `esri` to `Esri`
enum EsriShapeImportFlags --> all value prefixes changed from `esri` to `Esri`

Other enum changes:

enum GeometryDimension --> enum GeometryDimensionType
enum JSONExportFlags --> enum JsonExportFlags - all value prefixes changed from `json` to `Json`
enum JSONImportFlags --> enum JsonImportFlags - all value prefixes changed from `json` to `Json`
enum Monotonic --> enum MonotonicType
enum SegmentExtension --> enum SegmentExtensionType
enum WKBExportFlags --> enum WkbExportFlags  - all value prefixes changed from `WKB` to `Wkb`

At 3.0, the older generation of geometry and segment builders were removed. Addins constructing geometries and segments using the older builders will need to switch to using the newer "Ex" equivalents. Starting at 2.5+, the new generation of builders was introduced into the public API. These "newer" builders have the suffix "Ex" added to their name, so PolygonBuilderEx, PolylineBuilderEx, EnvelopeBuilderEx, and so on.

The newer geometry and segment builders remove the requirement for the builder to be created on the MCT*, meaning they can be run on any thread, not just the QueuedTask*. Builder "Ex"s are not IDisposable so 2.x code that scoped the lifetime of an older builder with a using(....) statement must remove the "using" when dealing with the newer builders. 2.x Addins already consuming the builder "Ex"s need make no changes.

//At        2.x          --->        3.0
class MapPointBuilder    --- > class MapPointBuilderEx
class MultipointBuilder  --- > class MultipointBuilderEx
 
class LineBuilder     --- > class LineBuilderEx
class EllipticArcBuilder    --- > class EllipticArcBuilderEx
class CubicBezierBuilder    --- > class CubicBezierBuilderEx
class PolylineBuilder    --- > class PolylineBuilderEx

class EnvelopeBuilder    --- > class EnvelopeBuilderEx
class PolygonBuilder    --- > class PolygonBuilderEx
class GeometryBagBuilder    --- > class GeometryBagBuilderEx

class MultipatchBuilder    --- > class MultipatchBuilderEx

//class SpatialReferenceBuilder is not replaced

Here is an example of 2.x addin code using the older builders converted to 3.0 code using the builder "Ex" equivalent:

  //2.x
  QueuedTask.Run(() => {
     //Builders at 2.x are IDisposable - can scope lifetime with a "using" block
     using (PolylineBuilder polylineBuilder = new PolylineBuilder(polyline)) { 
       polylineBuilder.ReverseOrientation();
       var reversedPolyline = polylineBuilder.ToGeometry();
     }
  });

   QueuedTask.Run(() => {
     //Run on the MCT
     var circularArc = EllipticArcBuilderEx.CreateEllipticArcSegment(segment1, segment2, maxRadius, hintPoint);
     //etc
   });

 //At 3.x - no using(...) - Builder "Ex" are not IDisposable
 var polylineBuilder = new PolylineBuilderEx(polyline);
 polylineBuilder.ReverseOrientation();
 var reversedPolyline = polylineBuilder.ToGeometry();

  // Most of the BuilderEx convenience methods do not need the MCT.
  var circularArc = EllipticArcBuilderEx.CreateCircularArc(
                          segment1, segment2, maxRadius, hintPoint);

  //However, this particular EllipticArcBuilderEx constructor _does_ need the MCT. Please refer 
  //to the API reference as needed. There are only a handful of such cases
  QueuedTask.Run(() => {
    var cab = new EllipticArcBuilderEx(segment1, segment2, maxRadius, hintPoint);
    var otherCircularArc = cab.ToSegment();
    //etc
  });

Note: SpatialReferenceBuilder is not replaced with an "Ex" equivalent. Consult ProConcepts: Geometry for more details on the Geometry API.

*There are still a handful of builder Ex methods and constructors that require the MCT. This is limited to the EllipticArcBuilderEx, PolylineBuilderEx and PolygonBuilderEx classes. The remaining thread affinity will be removed over the coming 3.x releases.

Licensing

At 3.0, the LicensingInformation.IsAvailable(licenseCode) method, deprecated at 2.8, was removed. Addins should use the LicenseInformation.IsCheckedOut(licenseCode) method,topic 72618, instead.

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.CoreHost.dll

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Catalog.dll

At 3.0, the return value from ArcGIS.Desktop.Catalog.OpenItemDialog.Items was changed from IEnumerable<Item> to IList<Item>.

ArcGIS.Desktop.Catalog.ItemFilters properties have been camel cased. So, for example:

//      At 2.x                  At 3.x
ItemFilters.annotation => ItemFilters.Annotation
ItemFilters.cad=>         ItemFilters.Cad
ItemFilters.composite_addToMap => ItemFilters.Composite_AddToMap 
...

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Core.dll

Cef and use of Cef sharp was deprecated at 2.9. At 3.0, Cef was no longer supported. The method, ProApp.InitializeCef() was removed.

The threading restrictions forArcGISPortal methods have changed:

ArcGIS.Desktop.Core.ArcGISPortal.SignIn(), topic 14680, has changed. Unless the caller is within the OnApplicationInitializing callback of a Configuration, SignIn() must be invoked on the QueuedTask. This thread restriction was not enforced at 2.x.

ArcGIS.Desktop.Core.ArcGISPortal.SignOut(), topic 14682, has changed. Unless the caller is within the OnApplicationInitializing callback of a Configuration, SignOut() must be invoked on the QueuedTask. This thread restriction was not enforced at 2.x.

ArcGIS.Desktop.Core.ArcGISPortal.GetSignOnUsername(), topic 14676, has changed. Unless the caller is within the OnApplicationInitializing callback of a Configuration, GetSignOnUsername() must be invoked on the QueuedTask. This thread restriction was not enforced at 2.x.

ArcGIS.Desktop.Core.ArcGISPortal.GetToken(), topic 14677, has changed. Unless the caller is within the OnApplicationInitializing callback of a Configuration, GetToken() must be invoked on the QueuedTask. This thread restriction was not enforced at 2.x.

Refer to ArcGIS.Desktop.Mapping.dll for changes to IMappableItem and IMappableItemEx relevant for custom item implementations at 3.0.

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.DataReviewer.dll

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Editing.dll

At 3.0, the derived template class ArcGIS.Desktop.Editing.Templates.EditingFeatureTemplate (deriving from ArcGIS.Desktop.Editing.Templates.EditingTemplate, topic 9918) was changed to ArcGIS.Desktop.Editing.Templates.EditingRowTemplate, topic 76314. This is to accommodate the support for templates on standalone tables introduced with 2.9. Edit operation overloads that consumed the underlying CIM definition, CIMEditingTemplate, topic 1174 for the EditingFeatureTemplate class have also changed. The majority of changes are name, spelling, and case changes to improve consistency. This includes the editing template extension methods available on the ArcGIS.Core.CIM.CIMExtensions class within the editing assembly. At 2.x, these methods were available via the ArcGIS.Core.CIM.EditingTemplateCIMExtensions class.

Addins using CIMFeatureTemplate, derived from CIMEditingTemplate at 2.x should change their references to CIMRowTemplate, topic 74946 at 3.x.

Examples follow:

 using ArcGIS.Desktop.Editing.Templates;
 ...

 // retrieve the CIM edit template definition - CIMEditingTemplate
 var template = mapTool.CurrentTemplate;
 var templateDef = template.GetDefinition();

 //Property and extension method name changes
 //At 2.x 
 templateDef.ToolProgID = toolContentGUID;
 var toolIds = cimEditTemplate.GetExcludedToolDamlIds().ToList();
 cimEditTemplate.SetExcludedToolDamlIds(toolIds.ToArray());
 cimEditTemplate.AllowToolDamlID("esri_editing_SketchLineTool");

 //get a template definition the feature layer
 var resTemplate = featLayer.GetTemplate("Residential");
 var resTempDef = resTemplate.GetDefinition() as CIMFeatureTemplate;
 //use:    var newTemplate = new CIMFeatureTemplate(); to create a new one      

 //EditOperation.Merge(...)
 mergeFeatures.Merge(this.CurrentTemplate as EditingFeatureTemplate, featureLayer, 
                                                       new List<long>() { 10, 96, 12 });

 //At 3.x
 templateDef.DefaultToolGUID = toolContentGUID;
 var toolIds = cimEditTemplate.GetExcludedToolIDs().ToList();
 cimEditTemplate.SetExcludedToolIDs(toolIds.ToArray());
 cimEditTemplate.AllowToolID("esri_editing_SketchLineTool");

 //get a template definition the feature layer
 var resTemplate = featLayer.GetTemplate("Residential");
 var resTempDef = resTemplate.GetDefinition() as CIMRowTemplate;
 //use:    var newTemplate = new CIMRowTemplate(); to create a new one

 //EditOperation.Merge(...)
 mergeFeatures.Merge(this.CurrentTemplate as EditingRowTemplate, featureLayer, 
                                                       new List<long>() { 10, 96, 12 });

At 2.x, the ArcGIS.Desktop.Editing.Attributes.Inspector default constructor has removed the optional bool isFeatureEditing = true flag. At 3.x, the Inspector will always be instantiated allowing features or rows to be updated.

 //at 2.x
 var insp = new Inspector(true | false);
 //At 3.x
 var insp = new Inspector();//isFeatureEditing = true is just assumed.

At 3.0, the EditOperation.Create overloads were changed to return a ArcGIS.Desktp.Editing.RowToken,topic 19317, instance that can be used to retrieve the ObjectID and GlobalID of the newly created feature (when the edit operation has executed successfully). At 2.x, to retrieve the object id required use of an Action<long> result parameter. The action parameter has now been removed at 3.x.

Note: retrieving the newly created feature/row object id is required when chaining edit operations.

//At 2.x
long newFeatureID = -1;
editOp.Create(featLayer, poly, (oid) => newFeatureID = oid);
if (editOp.Execute()) {
   //operation executed successfully
   WriteToLog($"{featLayer.Name}: new feature: {newFeatureID}");

//At 3.x
long newFeatureID = -1;
var rowToken = editOp.Create(featLayer, poly);
if (editOp.Execute()) {
   //operation executed successfully
   long newFeatureID = (long)rowToken.ObjectID;
   WriteToLog($"{featLayer.Name}: new feature: {newFeatureID}");

At 2.x, multiple EditOperation class methods consumed, as a parameter, an IEnumerable of MapMembers along with a corresponding list of object ids of the form IEnumerable<KeyValuePair<MapMember,List<long>>>. For example: EditOperation.Split, EditOperation.Move, EditOperation.Rotate, EditOperation.Reshape, EditOperation.Scale, and EditOperation.Delete amongst others. This IEnumerable parameter matched the selection output from MapView selection methods such as MapView.SelectFeatures, and MapView.SelectFeaturesEx. Starting at 3.0, MapView selections are supported by a SelectionSet class rather than by a generic .NET collection. Edit operations that previously consumed the generic .NET collection/IEnumerable at 2.x have been changed to consume the SelectionSet class at 3.0. SelectionSet related changes are also discussed in the ArcGIS.Desktop.Mapping.dll section.

 //At 2.x - construct an enumerable of key value pairs from the mapview selection
 var selFeatures = MapView.Active.GetFeatures(intersectPolygon).Select(
      k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));

 //pass to edit operation
 editOp.Split(selFeatures, ...);
 editOp.Move(selFeatures, ...);
 editOp.Rotate(selFeatures, origin, ...);
 editOp.Reshape(selFeatures, ...);
 editOp.Scale(selFeatures, ...);

 //At 3.x, MapView will return SelectionSet
 var selSet = MapView.Active.GetFeatures(intersectPolygon);

 //pass to edit operation
 editOp.Split(selSet, ...);
 editOp.Move(selSet, ...);
 editOp.Rotate(selSet, origin, ...);
 editOp.Reshape(selSet, ...);
 editOp.Scale(selSet, ...);

At 3.0, the editOperation.IsCancelled flag was changed to editOperation.IsCanceled .

At 3.0, editOperation.Duplicate(...) was removed. Addins using editOperation.Duplicate at 2.x, should use either a copy + move or a create + move edit operation method combination at 3.x.

 var editOp = new EditOperation();
 editOp.Name = "Duplicate Features";

 //At 2.x
 editOp.Duplicate(featureLayer, oid, 500.0, 500.0, 0.0);

 //At 3.x, use either of a copy + move or a create + move
 //in this example, an inspector is being used to access the feature attributes
 //
 var insp = new Inspector();
 insp.Load(featureLayer, oid);
 
 //make a copy using create + attributes
 var rtoken = editOp.Create(featureLayer, insp.ToDictionary(a => a.FieldName, a => a.CurrentValue));
 if (editOp.Execute())  {
    //do a move - chain to keep the two operations as a single undo
    var modifyOp = editOp.CreateChainedOperation();
    modifyOp.Modify(featureLayer, (long)rtoken.ObjectID, GeometryEngine.Instance.Move(geom, 500.0, 500.0));
    modifyOp.Execute();
 }

At 3.0, editOperation.Transform(...) and editOperation.TransformAffine(...) was replaced with an overload of editOperation.Transform(...), topic 9562, that consumes a TransformMethod. At 3.0 or better, use the derived type of TransformMethod - either TransformByLinkLayer or TransformByLinkLines - to define the transform parameters. Use transformMethod.TransformType to define the type of transformation to perform (TransformMethodType.Affine or TransformMethodType.Similarity).

        
 var editOp = new EditOperation();
 editOp.Name = "Transform Features";

 //At 2.x 
 var transformSelection = MapView.Active.GetFeatures(polygon).Select(
                      k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));
 editOp.Transform(transformSelection, linkLayer);
 //Transform just a layer
 editOp.Transform(featureLayer, linkLayer);
 //Perform an affine transformation
 editOp.TransformAffine(featureLayer, linkLayer);

 //At 3.x
 //Define the transform method
 var affine_transform = new TransformByLinkLayer() {
    LinkLayer = linkLayer,
    TransformType = TransformMethodType.Affine //or TransformMethodType.Similarity
 };
 //Transform a selected set of features
 editOp.Transform(MapView.Active.GetFeatures(polygon), affine_transform);
 //Perform an affine transformation
 editOp.Transform(featureLayer, affine_transform);

 //Execute to execute the operation
 //Must be called within QueuedTask.Run
 editOp.Execute();
 
 //or use async flavor
 //await editOp.ExecuteAsync();

At 2.x, construction tools could use an EmbeddableControl to host a custom configurable UI within the create features pane and template UI. The Embeddable control would implement IEditingCreateToolControl and IEditingCreateToolMultiple to host the embeddable control UI on the create features pane and/or the template property dialog UI.

At 3.0, the IEditingCreateToolControl and IEditingCreateToolMultiple was removed. Instead, construction tools that have configurable UIs (implemented within an Embeddable control) should derive from ToolOptionsEmbeddableControl for their embeddable controls and implement the relevant overrides.

\\At 2.x - embeddable controls associated with construction tools
\\implement IEditingCreateToolControl and IEditingCreateToolMultiple to
\\display configurable UIs
internal class CustomToolToolOptionsViewModel : EmbeddableControl, 
                IEditingCreateToolControl {//most common
   ...

internal class CustomToolToolOptionsViewModel : EmbeddableControl, 
      IEditingCreateToolControl, IEditingCreateToolMultiple {//less common - tool supports multiple templates
  ...


\\At 3.x, derive from ToolOptionsEmbeddableControl, no interfaces needed
internal class CustomToolToolOptionsViewModel : ToolOptionsEmbeddableControl {

 //Occurs when a tool options control is initialized
 public override void OnInitialize(ToolOptions options, bool hostIsActiveTmplatePane) {
    //TODO
    base.OnInitialize(options, hostIsActiveTmplatePane);
  }
  //Occurs when a tool options control is initialized
  public override void OnInitialize(IEnumerable<ToolOptions> optionsCollection, 
                                    bool hostIsActiveTmplatePane) {
    //TODO - handle when the tool options control is initialized
  }

  //Gets and sets the valid state of the tool options - default is true
  public virtual bool IsValid { get; set; } = true;

  //Gets and sets the dirty state of the tool options - default is false
  public virtual bool IsDirty { get; set; }

  //Gets and sets whether the tool options should auto open in the Active Template 
  //pane when the associated tool is activated - default is false
  public virtual bool IsAutoOpen(string toolID) => false;

  //Gets the icon to display when the tool options are displayed in the Active Template pane.
  public override ImageSource SelectorIcon => ... ;

  //Gets a working copy of the ToolOptions
  protected internal ToolOptions ToolOptions { get; }

  // Called at the end of <see cref="OpenAsync"/>, implementations should obtain and interpret the 
  //options stored in the current <see cref="ToolOptions"/> using
  //protected T GetToolOption<T>(string key, T defaultValue, T differentValue = default(T))
  protected override Task LoadFromToolOptions() {
      //eg, this tool checks if it has a stored default value for a "Buffer" option
      double? buffer = GetToolOption<double?>("Buffer", 25.0, null);
      if (buffer.HasValue) {//there is a default
         ...
      }
      return Task.CompletedTask;
    }

For IEditingCreateToolControl method implementations at 2.x of:

  • public virtual bool InitializeForActiveTemplate(ToolOptions options)
  • public virtual bool AutoOpenActiveTemplatePane(string toolID)

use these ToolOptionsEmbeddableControl overrides on your Embeddable control:

  • public override void OnInitialize(IEnumerable<ToolOptions> optionsCollection, bool hostIsActiveTemplatePane)
  • public override bool IsAutoOpen(string toolID)

In the Config.daml, the embeddable control is registered in the esri_editing_tool_options category and the daml-id is assigned to the toolOptionsID attribute in the content tag of the construction tool same as was done at 2.x.

ArcGIS.Desktop.Extensions.dll

At 3.0, the ArcGIS.Desktop.Extensions.Controls.BurgerButton control was removed. 2.x addins still referencing the BurgerButton control will need to switch over to ArcGIS.Desktop.Framework.Controls.BurgerButton. The "Framework" BurgerButton control replaced the original "Extensions" control in the 2.2, 2.3 timeframe.

The BurgerButton control is used within dockpanes for a popup menu. It will be referenced in the dockpane usercontrol xaml most likely. Here is an example:

<!-- change this at 2.x -->
<UserControl x:Class="DockPaneBookmarkAdvanced.BookmarkView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  ...                          
  xmlns:extensionsControls="clr-namespace:ArcGIS.Desktop.Extensions.Controls;assembly=ArcGIS.Desktop.Extensions"
  ... >

<!-- to this at 3.x -->
<UserControl x:Class="DockPaneBookmarkAdvanced.BookmarkView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  ...                          
  xmlns:extensionsControls="clr-namespace:ArcGIS.Desktop.Framework.Controls;assembly=ArcGIS.Desktop.Framework"
  ... >

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Framework.dll

At 3.0, the ArcGIS.Desktop.Framework.Controls.ChromiumWebBrowser control, which was deprecated at 2.9, was removed. Addins should use the ArcGIS.Desktop.Framework.Controls.WebViewBrowser control instead.

At 3.x, the behavior of the application title and name has changed. At 3.x, if a FrameworkApplication.Title is specified then the application title bar defaults to just showing the title and the name and sub-title are not shown:

//At 2.x, this code would show Name-Title-Subtitle
//At 3.x, only title will show.
 var date_time = System.DateTime.Now.ToString("g");
 FrameworkApplication.Name = date_time;
 FrameworkApplication.SubTitle = "MyMap";
 FrameworkApplication.Title = Project.Current.Name;

app-title1.png

//At 3.x, title is set to "", title defaults to FrameworkApplication.Name
 var date_time = System.DateTime.Now.ToString("g");
 FrameworkApplication.Name = date_time;
 FrameworkApplication.SubTitle = "MyMap";
 FrameworkApplication.Title = "";//no title

app-title2.png

//At 3.x Subtitle is ignored in the bar
 var date_time = System.DateTime.Now.ToString("g");
 FrameworkApplication.Name = "";//no name
 FrameworkApplication.SubTitle = "MyMap";
 FrameworkApplication.Title = "";//no title

app-title3.png

At 3.x, for Configurations and ConfigurationManager the new title behavior has ramifications for how the ConfigurationManager.ApplicationName is shown in the title bar. At 2.x, the ConfigurationManager.ApplicationName could be used to override the title bar text. However, at 3.x, because the title bar only shows the application title (if a title is defined), the configuration ApplicationName is ignored. To show the ApplicationName at 3.x, configurations should implement the new TitleBarText override.

Include the configManager.ApplicationName as part of the configManager.TitleBarText property. This is explained in detail in the ProConcepts Configurations, TitleBarText section.

At 3.x, the ArcGIS.Desktop.Framework.Dialogs.BrowseForFolder for browsing for folders (as opposed to browsing for files) has been removed. Addins should use the windows API and the System.Windows.Forms.FolderBrowserDialog instead. An example follows:

using System.Windows.Interop;// for WindowInteropHelper


//Get a handle to the Pro app window
var parentWindow = new WindowInteropHelper(
    FrameworkApplication.Current.MainWindow).Handle;

//At 2.x
//using ArcGIS.Desktop.Framework.Dialogs;
//
//var browseDialog = new BrowseForFolder();
//var selectedFolder = browseDialog.SelectFolder(
//      "Please select an output folder", @"C:\\The\\Initial\\Path", IntPtr.Zero);
//....or, use the Pro application window handle
//var selectedFolder = browseDialog.SelectFolder(
//     "Please select an output folder", @"C:\\The\\Initial\\Path", parentWindow);

//At 3.x
using System.Windows.Forms;
...

var parentWindow = new WindowInteropHelper(
    FrameworkApplication.Current.MainWindow).Handle;

var browseDialog = new FolderBrowserDialog() {
  InitialDirectory = @"C:\\The\\Initial\\Path",
  Description = "Please select an output folder",
  UseDescriptionForTitle = true
};
var selectedFolder = "";
if (folderBrowserDialog1.ShowDialog(
      new WindowWrapper(parentWindow)) == DialogResult.OK) {
   selectedFolder = folderBrowserDialog1.SelectedPath;
}
//TODO - use selected folder path
...

// http://stackoverflow.com/questions/315164/how-to-use-a-folderbrowserdialog-from-a-wpf-application
public class WindowWrapper : System.Windows.Forms.IWin32Window {
  IntPtr _handle = IntPtr.Zero;

  public WindowWrapper(IntPtr handle) {
    _handle = handle;
  }
 IntPtr System.Windows.Forms.IWin32Window.Handle => _handle;
}

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Geoprocessing.dll

At 3.0, ArcGIS.Desktop.Geoprocessing.HistoryProjectItem, deprecated at 2.1, was removed.

Consult What's New in the API reference for the complete list of API changes at 3.0.

Esri.ArcGIS.ItemIndex.dll

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Layout.dll

At 3.0, LayoutElementFactory was renamed to ElementFactory, topic 76396.

Also, at 2.x, LayoutElementFactory provided a large number of very coarse-grained macros for GraphicElement creation (i.e. point, line, polygon, and text graphic elements). These were consolidated into two, generic, macros: CreateGraphicElement for points, lines, and polygons and CreateTextGraphicElement for creation of text. CreateGraphicElement and CreateTextGraphicElement also take a new parameter class at 3.x, ElementInfo, allowing some initial properties of elements to be specified up-front, at the time of element creation.

At 2.x, separate LayoutElementFactory macro overloads were required for use with Layouts, GroupElements, and GraphicsLayer as the different containers. Layout and GroupElement macros had a first argument of type ILayoutElementContainer and GraphicsLayer had a first argument of type GraphicElementContainer. At 3.x, ILayoutElementContainer and GraphicElementContainer have been replaced by a single container of type IElementContainer, topic 76912 so there is no need to differentiate between the different containers at 3.x, when using ElementFactory.

Examples follow:

//At 2.x
QueuedTask.Run(()=> {
   //Create point, line, polygon graphic elements - we typically match specific geometries 
   //with specific macros. Overloads for layout, group element, graphics layer as the container
   LayoutElementFactory.Instance.CreateGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateGraphicElement(layout, graphic);
   LayoutElementFactory.Instance.CreatePointGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateLineGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreatePolygonGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateBezierCurveGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateCircleGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateEllipseGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateFreehandGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateLassoGraphicElement(layout, ...);
   LayoutElementFactory.Instance.CreateRectangleGraphicElement(layout, ...);

   //At 3.x use CreateGraphicElement with (optional) ElementInfo. No need for separate
   //overloads for layout, group element, graphics layer as the container at 3.x
   var container = ....;//Layout, GroupElement, or GraphicsLayer
   
   ElementFactory.Instance.CreateGraphicElement(container, bezPl, lineSym, "New Bezier Curve");
   ElementFactory.Instance.CreateGraphicElement(container, linePl, lineSym, "New Freehand");
   ElementFactory.Instance.CreateGraphicElement(container, env, polySym, "New Polygon");
   
   //and with ElemInfo...
   var elemInfo = new ElementInfo() {
     Anchor = Anchor.CenterPoint,
     Rotation = 45
   };
   ElementFactory.Instance.CreateGraphicElement(container, env, polySym, "New Polygon", true, elemInfo);

   //Or, instead of a geometry + symbol, use a CIMGraphic
   var graphic = new CIMPointGraphic() { ..... };
   ElementFactory.Instance.CreateGraphicElement(container, graphic);

   //At 2.x - create text graphic elements
   LayoutElementFactory.Instance.CreateCurvedTextGraphicElement(container, ...);
   LayoutElementFactory.Instance.CreatePointTextGraphicElement(container, ...);
   LayoutElementFactory.Instance.CreateCircleParagraphGraphicElement(container, ...);
   LayoutElementFactory.Instance.CreatePolygonParagraphGraphicElement(container, ...);
   LayoutElementFactory.Instance.CreateEllipseParagraphGraphicElement(container, ...);
   LayoutElementFactory.Instance.CreateRectangleParagraphGraphicElement(container, ...);

   //At 3.x - use CreateTextGraphicElement and TextType enum to specify the type of
   //text to create
   ElementFactory.Instance.CreateTextGraphicElement(
      container, TextType.SplinedText, bezPl, sym, "Curved Text", "New Splined Text");
   ElementFactory.Instance.CreateTextGraphicElement(
      container, TextType.RectangleParagraph, poly, sym, text, "New Polygon Text");
   ElementFactory.Instance.CreateTextGraphicElement(
      container, TextType.CircleParagraph, polyCir, sym, text, "New Circle Text");

   //and with ElementInfo
   var elemInfo = new ElementInfo() {
      Anchor = Anchor.CenterPoint,
      Rotation = 45
   };
   ElementFactory.Instance.CreateTextGraphicElement(
          container, TextType.PointText, coord2D.ToMapPoint(), sym, textString, "New Point Text", true, elemInfo);

   //At 2.x - create map frame and associated surrounds
   LayoutElementFactory.Instance.CreateMapFrame(layout, env, mfMap);

   LayoutElementFactory.Instance.CreateLegend(layout, env, mapFrame);
   LayoutElementFactory.Instance.CreateScaleBar(layout, center, mapFrame, sbStyleItm);
   LayoutElementFactory.Instance.CreateNorthArrow(layout, center, mapFrame, naStyleItm);
   LayoutElementFactory.Instance.CreateTableFrame(layout, env, mapFrame, featLayer, layerFields);
   LayoutElementFactory.Instance.CreateChartFrame(layout, ...);

   //At 3.x, use CreateMapFrameElement and CreateMapSurroundElement along with the
   //relevant ArcGIS.Desktop.Layouts.MapSurroundInfo
   var mfElm = ElementFactory.Instance.CreateMapFrameElement(layout, env, map, "New Map Frame");
   
   var legInfo = new LegendInfo() { MapFrameName = mfElm.Name};
   var sbInfo = new ScaleBarInfo() { MapFrameName = mfElm.Name, ScaleBarStyleItem = sbStyleItm};
   var naInfo = new NorthArrowInfo() { MapFrameName = mfElm.Name, NorthArrowStyleItem = naStyleItm};
   var tfInfo = new TableFrameInfo() { MapFrameName = mfElm.Name, 
                                          MapMemberUri = featLayer.URI, FieldNames = layerFields};
   var cfInfo = new ChartFrameInfo() { MapFrameName = mfElm.Name, MapMemberUri = featLayer.URI};

   var legendElm = ElementFactory.Instance.CreateMapSurroundElement(
                         layout, env, legInfo, "New Legend") as Legend;
   var sbElm = ElementFactory.Instance.CreateMapSurroundElement(
                      layout, center.ToMapPoint(), sbInfo, "New Scale Bar") as ScaleBar;
   var arrowElm = ElementFactory.Instance.CreateMapSurroundElement(
                         layout, center.ToMapPoint(), naInfo, "New North Arrow") as NorthArrow;
   var tabFrame = ElementFactory.Instance.CreateMapSurroundElement(
                         layout, env, tfInfo) as TableFrame;
   var chartFrame = ElementFactory.Instance.CreateMapSurroundElement(
                         layout, env, cfInfo) as ChartFrame;

   //At 2.x - CreateGroupElement has an overload that takes a single element (to be "grouped)
   LayoutElementFactory.Instance.CreateGroupElement(container, titleElm);

   //At 3.x, a list of elements to be grouped must be provided
   ElementFactory.Instance.CreateGroupElement(container, new List<Element>() { titleElm });

At 3.x, Layout and element events have been consolidated to reduce the different number of events an addin developer has to consume (to listen for layout and element state and context changes). At 3.x, addin developers need only subscribe to three events: LayoutEvent topic76584.html, LayoutViewEvent topic18642.html and ElementEvent topic76570.html.

For LayoutAdded and LayoutRemoved equivalency, addins should use ArcGIS.Desktop.Core.Events.ProjectItemsChangedEvent at 3.x.

 //Layout events
 //   At 2.x         --        At 3.x
 LayoutAddedEvent       ProjectItemsChangedEvent, hint: NotifyCollectionChangedAction.Add
 LayoutRemovedEvent     ProjectItemsChangedEvent, hint: NotifyCollectionChangedAction.Remove
 LayoutChangedEvent     LayoutEvent, hint: LayoutEventArgs.LayoutEventHint
 LayoutClosing          LayoutViewEvent, hint: LayoutViewEventArgs.LayoutViewEventHint.Closing
 LayoutClosed           LayoutViewEvent, hint: LayoutViewEventArgs.LayoutViewEventHint.Closed

 LayoutPauseDrawingChangedEvent       LayoutViewEvent, 
                            hint: LayoutViewEventArgs.LayoutViewEventHint.PauseDrawingChanged

 LayoutViewEvent        LayoutViewEvent, hint: LayoutViewEventArgs.LayoutViewEventHint
 MapSeriesEvent         LayoutEvent, hint: LayoutEventArgs.LayoutEventHint
 PageChangedEvent       LayoutEvent, hint: LayoutEventArgs.LayoutEventHint

 ActiveLayoutViewChangedEvent  LayoutViewEvent, 
                        hint: LayoutViewEventArgs.LayoutViewEventHint.Activated, Deactivated

 ActiveMapFrameEvent    **ElementEvent** (not LayoutViewEvent)
                     hint: ElementEventArgs.ElementEventHint.MapFrameActivated, MapFrameDeactivated

 //Element events
 //   At 2.x         --        At 3.x
 ElementAddedEvent      ElementEvent, hint: ElementEventArgs.ElementEventHint.ElementAdded
 ElementRemovedEvent    ElementEvent, hint: ElementEventArgs.ElementEventHint.ElementRemoved
 ElementsUpdatedEvent   ElementEvent, hint: ElementEventArgs.ElementEventHint

 ElementStyleChangedEvent  ElementEvent, hint: ElementEventArgs.ElementEventHint.StyleChanged
 ElementsPlacementChangedEvent  ElementEvent, hint: ElementEventArgs.ElementEventHint.PlacementChanged

 //At 2.x
 LayoutSelectionChangedEvent    (deprecated at 2.6)
 ArcGIS.Desktop.Mapping.Events.ElementSelectionChangedEvent

 // At 3.x use:
 ElementEvent, hint: ElementEventArgs.ElementEventHint.SelectionChanged

At 3.x, ReportElementFactory macros has been changed to consume a container of type IElementContainer.

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Mapping.dll

At 3.0, LayerFactory create layer macros were consolidated around the use of the LayerCreationParams class and associated derived creation "param" classes. The "param" classes offer a greater range of creation properties than the original individual create layer macros (that they replace). The populated "param" classes are passed to the CreateLayer<t> overload to create the layer. The "T", or templated type, should be replaced with whatever is the layer type to be created (and should match the appropriate layer creation "param" class). Addins calling any of the 2.x create "XXX" layer macros will need to switch to using the LayerCreationParam pattern at 3.x.

At 3.x, LayerCreationParams now includes MapMemberIndex and MapMemberPosition properties. Previously, at 2.x, these would have been set as individual parameters on the various LayerFactory create layer macro overloads.

Examples follow:

 //At 2.x CreateLayer<T>(...) does not include a LayerPosition position default parameter
 //At 3.0, addins should use the LayerCreationParams MapMemberPosition instead.

 //At 2.x - Addins using
 LayerFactory.Instance.CreateLayer(item, ....);
 LayerFactory.Instance.CreateLayer(cimDataConnection, ....);

 //should use layer creation params.
 //Note: set MapMemberIndex or MapMemberPosition on the LayerCreationParams
 //if the addin was using the relevant parameter at 2.x
 var layerParams = new LayerCreationParams(item | cimDataConnection) { 
                         MapMemberIndex = -1 
                       };
 var layer = LayerFactory.Instance.CreateLayer<Layer>(layerParams, ...);
 
 //Note, 3.x provides the following overload of CreateLayer for scenarios where
 //addins want to create a layer via a path or Uri to the underlying data source
 Layer CreateLayer(Uri dataUri, ILayerContainerEdit container, int index = 0, string layerName = "");

 //At 2.x - Addins using
 LayerFactory.Instance.CreateFeatureLayer(featClass, ....);
 LayerFactory.Instance.CreateFeatureLayer(item, ....);
 LayerFactory.Instance.CreateFeatureLayer(uri, ....);
 LayerFactory.Instance.CreateFeatureLayer(cimDataConnection, ....);

 //At 3.x
 //use feature layer creation params
 //Note: set MapMemberIndex, MapMemberPosition, and RendererDefinition on the 
 //FeatureLayerCreationParams if the addin was using the relevant Create method parameter at 2.x
 var layerParams = new FeatureLayerCreationParams(featClass |item | uri | cimDataConnection) { 
                           MapMemberPosition = MapMemberPosition.AddToBottom 
                       };
 var layer = LayerFactory.Instance.CreateLayer<FeatureLayer>(layerParams, ...);

 //At 2.x - Addins using
 LayerFactory.Instance.CreateRasterLayer(item, ....);
 LayerFactory.Instance.CreateRasterLayer(uri, ....);
 LayerFactory.Instance.CreateRasterLayer(cimDataConnection, ....);

 //At 3.x, should use raster layer creation params.
 //Note: set MapMemberIndex, MapMemberPosition, and ColorizerDefinition on the 
 //RasterLayerCreationParams if the addin was using the relevant Create method parameter at 2.x
 var layerParams = new RasterLayerCreationParams(raster |item | uri | cimDataConnection) { 
                           MapMemberPosition = MapMemberPosition.AddToBottom 
                       };
 var layer = LayerFactory.Instance.CreateLayer<BasicRasterLayer>(layerParams, ...);
 
 //At 2.x - Addins using
 LayerFactory.Instance.CreateMosaicLayer(item, ....);
 LayerFactory.Instance.CreateMosaicLayer(uri, ....);
 LayerFactory.Instance.CreateMosaicLayer(cimDataConnection, ....);

 //At 3.x, should use mosaic layer creation params.
 //Note: set MapMemberIndex, MapMemberPosition, and ColorizerDefinition on the 
 //MosaicLayerCreationParams if the addin was using the relevant Create method 
 //parameter at 2.x
 var layerParams = new MosaicLayerCreationParams(item | uri | cimDataConnection) { 
                           MapMemberPosition = MapMemberPosition.AddToBottom 
                       };
 var layer = LayerFactory.Instance.CreateLayer<MosaicLayer>(layerParams, ...);

 //At 3.x CreateGroupLayer is unchanged
 LayerFactory.Instance.CreateGroupLayer(map, 0, "Group 1");//same as was at 2.x

At 3.x, StandaloneTableFactory method overloads have been consolidated into a CreateStandaloneTable method that consumes StandaloneTableCreationParams. The CreateStandaloneTable convenience overload that takes a URI still remains at 3.x.

Examples follow:

 //At 2.x
 //use a local path uri
 var table = StandaloneTableFactory.Instance.CreateStandaloneTable(
            new Uri(@"C:\Temp\Data\SDK.gdb\EarthquakeDamage", UriKind.Absolute), map);

 //GDB table
 var table2 = StandaloneTableFactory.Instance.CreateStandaloneTable(gdb_table, map);
 //Item
 var item = ItemFactory.Instance.Create(@"C:\Temp\Data\SDK.gdb\ParcelOwners");
 var table3 = StandaloneTableFactory.Instance.CreateStandaloneTable(item, map);

 //StandaloneTableCreationParams, eg with Item
 var tableParams = new StandaloneTableCreationParams(item);
 var table4 = StandaloneTableFactory.Instance.CreateStandaloneTable(tableParams, map);

 //At 3.x
 //use a local path uri
 var table = StandaloneTableFactory.Instance.CreateStandaloneTable(
            new Uri(@"C:\Temp\Data\SDK.gdb\EarthquakeDamage", UriKind.Absolute), map);
 
 //At 3.x
 //Use StandaloneTableCreationParams - Uri, Item, Table, CIMDataConnection, CIMLayerDocument
 var tableCreationParams = new StandaloneTableCreationParams(uri);
 var tableCreationParams2 = new StandaloneTableCreationParams(gdb_table);
 var tableCreationParams3 = new StandaloneTableCreationParams(dc);
 var tableCreationParams4 = new StandaloneTableCreationParams(layerDoc);
        
 var table = StandaloneTableFactory.Instance.CreateStandaloneTable(tableCreationParams, map);

At 3.x, BasicFeatureLayer definition queries have changed. Same as 2.x, definition query functionality is provided via the ITableDefinitionQueries interface. Definition queries were edited and defined through that interface using the CIMDefinitionFilter class, topic 21490. Also at 3.0, the public CIMDefinitionFilter[] BasicFeatureLayer.DefinitionFilterChoices property, deprecated at 2.5, has been removed.

At 3.x, CIMDefinitionFilter is stil public but a new model class, DefinitionQuery, very similar in its definition to CIMDefinitionFilter, is utilized to define a definition filter instead.

//At 2.x, using CIMDefinitionFilter
var defFilter = new CIMDefinitionFilter() {
  Name = "California",
  DefinitionExpression = "STATE_NAME = 'California'"
};

//At 3.x, using DefinitionQuery
var defQuery = new DefinitionQuery() {
  Name = "California",
  WhereClause = "STATE_NAME = 'California'"
};

Here are the 2x and 3.x declarations of ITableDefinitionQueries for comparison. Note how the 3.x flavor is DefinitionQuery based than on CIMDefinitionFilter as was in the 2x flavor:DefinitionQuery

//At 2.x ITableDefinitionQueries uses CIMDefinitionFilter
public class BasicFeatureLayer : Layer, ..., ITableDefinitionQueries {
   //Deprecated at 2.5. Use GetDefinitionFilters()
   public CIMDefinitionFilter[] DefinitionFilterChoices { get; }
}

//Defines required properties and methods for interacting with definition filters
public inteface ITableDefinitionQueries {
  CIMDefinitionFilter DefinitionFilter { get; }
  void SetDefinitionFilter(CIMDefinitionFilter activeFilter);
  IReadOnlyList<CIMDefinitionFilter> GetDefinitionFilters();//replaced DefinitionFilterChoices
  void SetDefinitionFilters(IEnumerable<CIMDefinitionFilter> filters, string activeFilterName = null);
  void RemoveDefinitionFilter(string filterName);
  void RemoveAllDefinitionFilters();
}

//At 3.x ITableDefinitionQueries uses DefinitionQuery
public class BasicFeatureLayer : Layer, ..., ITableDefinitionQueries {
  //DefinitionFilterChoices, deprecated at 2.5, is removed...
}

//Defines required properties and methods for interacting with definition filters
public inteface ITableDefinitionQueries {
  string DefinitionQuery { get; }
  IReadOnlyList<DefinitionQuery> DefinitionQueries { get; }
  DefinitionQuery SetDefinitionQuery(string whereClause);
  DefinitionQuery ActiveDefinitionQuery { get; } //The def query currently active
  void InsertDefinitionQuery(DefinitionQuery definitionQuery, bool makeActive = false);
  void InsertDefinitionQueries(IEnumerable<DefinitionQuery> queries);
  void RemoveActiveDefinitionQuery();
  void RemoveDefinitionQuery(int index);
  void RemoveDefinitionQueries(IEnumerable<string> queryNames);
  void RemoveAllDefinitionQueries();
}

An example usage follows:

 var us_states = MapView.Active.Map.GetLayersAsFlattenedList()
                     .OfType<FeatureLayer>().First(lyr => lyr.Name == "US States");

 QueuedTask.Run(() => {
    us_states.SetDefinitionQuery("STATE_NAME = 'Iowa'");
    //equivalent to...
    var def_query = new DefinitionQuery() {
      Name = "A Name that is Unique",
      WhereClause = "STATE_NAME = 'Iowa'"
   };
   us_states.InsertDefinitionQuery(def_query, true);

   var active_def_query = us_states.ActiveDefinitionQuery;
   string where_clause = us_states.DefinitionQuery;//just the where clause string
   
  //remove the active query
  us_states.RemoveActiveDefinitionQuery();

  //remove using name
  us_states.RemoveDefinitionQueries(new List<string> { def_query.Name });

  //use LINQ to get just a specific query
  var def_query = us_states.DefinitionQueries.FirstOrDefault(
                                         dq => dq.Name == "Query def name");
  ...

Note that, at 3.x, the FeatureLayerCreationParams DefinitionFilter property has been changed to DefinitionQuery in conjunction with the change from using CIMDefinitionFilter to DefinitionQuery to define layer definition filters as described above.

//At 2.x, DefinitionFilter was used with FeatureLayerCreationParams to define
//a definition filter upfront
public class FeatureLayerCreationParams : LayerCreationParams {
  ...
  public CIMDefinitionFilter DefinitionFilter { get; set; }

//At 3.x, use DefinitionQuery with FeatureLayerCreationParams instead
public class FeatureLayerCreationParams : LayerCreationParams {
  ...
  public DefinitionQuery DefinitionQuery { get; set; }

//define a definition query on the feat layer params
var featlayerParams = new FeatureLayerCreationParams() {
      DefinitionQuery = new DefinitionQuery("Name = 'Ponderosa Pine'", "My query1"),
      ...

At 3.x, VoxelLayer has been changed. At 3.x, the voxel layer API supports interacting with multiple volumes on the voxel layer via its VoxelLayer.GetVolumes method and via the voxelLayer.SelectedVariableProfile.Volume property. Slices and sections are now accessed of their associated volume at 3.x (and not off the voxel layer as was the case at 2.x). The following examples summarize the voxel layer API changes:

//At 2.x this was the common pattern - most everything off the layer
var voxelLayer = ...
var vol_size = voxelLayer.GetVolumeSize();
var section = voxelLayer.GetSections().First();
var slice = voxelLayer.GetSlices().First();
... etc...
//At 3.x, most everything is off the volume - so this is the most common pattern:
var voxelLayer = ...
var volume = voxelLayer.SelectedVariableProfile.Volume;
var vol_size = volume.GetVolumeSize();
var section = volume.GetSections().First();
var slice = volume.GetSlices().First();
... etc...

More examples:

 //At 2.x
 var volume = voxelLayer.GetVolumeSize();
 var x_max = volume.Item1;
 var y_max = volume.Item2;
 var z_max = volume.Item3;

 //At 3.x, there can be more than one volume - use GetVolumes()
 var x_max = voxelLayer.GetVolumes().Max(v => v.GetVolumeSize().X);
 var y_max = voxelLayer.GetVolumes().Max(v => v.GetVolumeSize().Y);
 var z_max = voxelLayer.GetVolumes().Max(v => v.GetVolumeSize().Z);

 //At 2.x
 //use voxelLayer to get slices, create slices
 var slices = voxelLayer.GetSlices();
 ...
 voxelLayer.UpdateSlice(slice);//Update slice via its layer

 //Create a slice on the voxel layer
 voxelLayer.CreateSlice(new SliceDefinition() {
     Name = "Middle Slice",
     VoxelPosition = new Coordinate3D(volume.Item1 / 2, volume.Item2 / 2, volume.Item3 / 2),
     ...

 //Create a section on the voxel layer
 voxelLayer.CreateSection(new SectionDefinition() {
      Name = $"Diagonal {s + 1}",
      VoxelPosition = new Coordinate3D(end_pt.X, end_pt.Y, volumeSize.Item3),
      ....

 //Access sections from the voxel layer
 foreach (var section in voxelLayer.GetSections()) {
    //set each normal to 45.0 orientation and tilt
    section.Normal = voxelLayer.GetNormal(45.0, 45.0);
    //apply the change via the voxel layer
    voxelLayer.UpdateSection(section);

 //Get a list of sections from the voxel layer that are 
 //not currently visible 
 var sections = voxelLayer.GetSections().Where(s => !s.IsVisible);

				
 //At 3.x, 
 //Use the SelectedVariableProfile to get the slices currently in the TOC
 //via its associated volume
 var volume = voxelLayer.SelectedVariableProfile.Volume;
 var slices = volume.GetSlices();
 ...
 volume.UpdateSlice(slice);//Update slice via its volume

 //Create a slice on the voxel -volume-
 var volume = voxelLayer.SelectedVariableProfile.Volume;
 var vol_size = volume.GetVolumeSize();

 volume.CreateSlice(new SliceDefinition() {
    Name = "Middle Slice",
    VoxelPosition = new Coordinate3D(vol_size.X / 2, vol_size.Y / 2, vol_size.Z/ 2),
    ...

 //Create a section on the voxel -volume-
 volume.CreateSection(new SectionDefinition() {
      Name = $"Diagonal {s + 1}",
      VoxelPosition = new Coordinate3D(end_pt.X, end_pt.Y, volumeSize.Z),
      ....

 //Access sections from the voxel -volume-
 foreach (var section in volume.GetSections()) {
    //set each normal to 45.0 orientation and tilt
    section.Normal = voxelLayer.GetNormal(45.0, 45.0);
    //apply the change via the voxel layer
    volume.UpdateSection(section);

 //Get a list of sections from the voxel -volume- that are 
 //not currently visible 
 var sections = volume.GetSections().Where(s => !s.IsVisible);

At 3.x, the variable.GetVariableStatistics() method has been removed. Use the VoxelVariableProfile.Statistics property to access voxel variable profile statistics instead.

 //At 2.x
 var variable = voxel.SelectedVariableProfile;
 var min = variable.GetVariableStatistics().MinimumValue;
 var max = variable.GetVariableStatistics().MaximumValue;

 //At 3.x
 var variable = voxel.SelectedVariableProfile;
 var min = variable.Statistics.MinimumValue;
 var max = variable.Statistics.MaximumValue;

At 3.x, the ArcGIS.Desktop.Mapping.Events.MapMemberPropertiesChangedEvent, the args.EventHints property, the hint MapMemberEventHint.VoxelSelectedVariableProfileIndex has been changed to MapMemberEventHint.VoxelSelectedVariable.

//At 2.x
  MapMemberPropertiesChangedEvent.Subscribe((args) => {
     var voxel = args.MapMembers.OfType<VoxelLayer>().FirstOrDefault();
     if (voxel == null)
       return;
  //Anything changed on a voxel layer?
  if (args.EventHints.Any(hint => hint == 
         MapMemberEventHint.VoxelSelectedVariableProfileIndex)) {
    ...

//At 3.x
  MapMemberPropertiesChangedEvent.Subscribe((args) => {
     var voxel = args.MapMembers.OfType<VoxelLayer>().FirstOrDefault();
     if (voxel == null)
       return;
  //Anything changed on a voxel layer?
  if (args.EventHints.Any(hint => hint == 
         MapMemberEventHint.VoxelSelectedVariable)) {
   ...

At 3.x, ElevationSurface and ElevationSource are removed and Elevation surfaces are no longer retrieved from a scene as CIMMapElevationSurface instances. At 3.x, Elevation surfaces and sources are represented by ElevationSurfaceLayer and (generic) layers (eg a raster, TIN, etc.) respectively. An ElevationSurfaceLayer is derived from CompositeLayer and can contain one or more child elevation source layers.

At 3.x, the MapElevationSurfaceDefinition class is removed. Use LayerFactory.Create<T> with an ElevationLayerCreationParams class instance instead.

 //At 2.x - 
 //If you used....
 var cimSurfaces = map.GetElevationSurfaces() ; //IReadOnlyList<CIMMapElevationSurface> 
 var cimSurface = map.GetGroundElevationSurface() ; //CIMMapElevationSurface

 map.SetElevationSurace(cimSurface);//or mapElevationSurfaceDefinition.ToCIM()
 map.SetElevationSuraces(cimSurfacesList);

 map.RemoveElevationSurface(cimSurface);
 map.ClearElevationSurfaces();
 
 //At 3.x use ElevationSurfaceLayer
 var surfaceLayers = map.GetElevationSurfaceLayers();

 var surfaceLayer = map.GetGroundElevationSurfaceLayer();

 //At 3.x Create surfaces using LayerFactory and ElevationLayerCreationParams
 //Surfaces will be created as ElevationMode.CustomSurface
 LayerFactory.Instance.CreateLayer<ElevationSurfaceLayer>(
       new ElevationLayerCreationParams(source_uri), scene);

 //Or create a new scene from scratch with a ground elevation source
 var scene = MapFactory.Instance.CreateScene(
                "My scene", groundSourceUri, MapViewingMode.SceneGlobal);

 //At 3.x, use map.RemoveLayer(s)(...) or ClearElevationSurfaceLayers
 map.RemoveLayer(surfaceLayer);//Cannot remove ground
 map.RemoveLayers(map.GetElevationSurfaceLayers()); //Ground will not be removed

 map.ClearElevationSurfaceLayers();

At 3.x, map.GetZsFromSurfaceAsync overloads have been changed. At 2.x, certain of the overloads consumed asurfaceName for the surface to be used (if other than ground) for the elevation query. Now, at 3.x, for the relevant overloads, use the particular elevationSurfaceLayer instance identifying the relevant surface instead.

//At 2.x - if using elevation surface -name-...
var surfaceZResult = await map.GetZsFromSurfaceAsync(
                             pt_location, "custom_elevation_surface");

//At 3.x - use elevation surface -layer-
var elevSurfaceLayer = map.GetElevationSurfaceLayers()
                             .First(sl => sl.Name == "custom_elevation_surface");
var surfaceZResult = await map.GetZsFromSurfaceAsync(pt_location, elevSurfaceLayer);

At 3.x, the interface ISceneLayerInfo has been removed. Addins previously using the ISceneLayerInfo.SceneServiceLayerType property should use the class type of the layer and/or FeatureSceneLayerType (in the case of FeatureSceneLayer) at 3.x. Also at 3.x, GetDataSourceType() and GetDataConnection() can be accessed off the layer directly.

At 3.x, PointCloudSceneLayer QueryAvailableClassCodesAndLabels, QueryAvailableClassFlagsAndLabels, and QueryAvailablePointCloudRendererFields methods are renamed to GetAvailableClassCodesAndLabels, GetAvailableClassFlagsAndLabels, and GetAvailablePointCloudRendererFields respectively.

At 3.x, BuildingScenelayer QueryAvailableFieldsAndValues is renamed to GetAvailableFieldsAndValues. SetFilter method is renamed to UpdateFilter.

  //At 2.x - using ISceneLayerInfo, eg on PointCloudSceneLayer
  var slInfo = pointCloudLayer as ISceneLayerInfo;
  if (slInfo.SceneServiceLayerType == SceneServiceLayerType.PointCloud) {
    //TODO - access Point cloud scene layer...

  var dataSourceType = slInfo.GetDataSourceType();
  var dc = slInfo.GetDataConnection();

 //At 2.x 
 var classCodesAndLabels = pcsl.QueryAvailableClassCodesAndLabels();
 var classFlagsAndLabels = pcsl.QueryAvailableClassFlagsAndLabels();
 var flds = pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.UniqueValueRenderer);

 //At 2.x BuildingScenelayer
 var classFieldsAndValues = bsl.QueryAvailableFieldsAndValues();

 var filterBlock = new FilterBlockDefinition();
 filterBlock.FilterBlockMode = Object3DRenderingMode.Wireframe;
 var selValues = new Dictionary<string, List<string>>();
 selValues ["Category"] = new List<string>() { ... };
 filterBlock.SelectedValues =  selValues;
 ...
 var filter1 = bsl.GetFilter(filter_id);
 filter1.Name = "Updated Filter";
 filter1.FilterBlockDefinitions = new List<FilterBlockDefinition>() { filterBlock };
 //At 2.x
 bsl.SetFilter(filter1);
 
 //At 3.x - ISceneLayerInfo is deleted. Use the layer class directly.
  var slInfo = pointCloudLayer as ISceneLayerInfo;
  if (sceneLayer is PointCloudSceneLayer pcsl) {
    //TODO - access Point cloud scene layer...

  var dataSourceType = pcsl.GetDataSourceType();//Off layer
  var dc = pcsl.GetDataConnection();//Off layer

 //At 3.x
 var classCodesAndLabels = pcsl.GetAvailableClassCodesAndLabels();
 var classFlagsAndLabels = pcsl.GetAvailableClassFlagsAndLabels();
 var flds = pcsl.GetAvailablePointCloudRendererFields(PointCloudRendererType.UniqueValueRenderer);

 //At 3.x BuildingScenelayer
 var classFieldsAndValues = bsl.GetAvailableFieldsAndValues();

 var filterBlock = new FilterBlockDefinition();
 filterBlock.FilterBlockMode = Object3DRenderingMode.Wireframe;
 var selValues = new Dictionary<string, List<string>>();
 selValues ["Category"] = new List<string>() { ... };
 filterBlock.SelectedValues =  selValues;
 ...
 var filter1 = bsl.GetFilter(filter_id);
 filter1.Name = "Updated Filter";
 filter1.FilterBlockDefinitions = new List<FilterBlockDefinition>() { filterBlock };
 //At 3.x
 bsl.UpdateFilter(filter1);

At 3.x, the IMappableItem and IMappableItemEx interfaces for use by Custom Items have been replaced with a single IMappableItem interface. Notice that the return type from the OnAddToMap overloads has been changed to type List<string>. Custom items should change their implementations of IMappableItem and IMappableItemEx at 2.x to the 3.x definition of IMappableItem. At 3.x, addins should return, from OnAddToMap, a list of the map member URIs that were added (in OnAddToMap). Refer to ProConcepts, Custom-Items adding item content to the map for more details.

 //At 2.x
 interface IMappableItem
 public bool CanAddToMap(MapType? mapType)
 public void OnAddToMap(Map map)
 public void OnAddToMap(Map map, ILayerContainerEdit groupLayer, int index)
 
 interface IMappableItemEx
 public string[] OnAddToMapEx(Map map)
 public string[] OnAddToMapEx(Map map, ILayerContainerEdit groupLayer, int index)

 //At 3.x -single (consolidated) interface IMappableItem
 interface IMappableItem
 public bool CanAddToMap(MapType? mapType)//No change
 public List<string> OnAddToMap(Map map)
 public List<string> OnAddToMap(Map map, ILayerContainerEdit groupLayer, int index)

  public bool CanAddToMap(MapType? mapType) {
    ... //No change

  public List<string> OnAddToMap(Map map) {
    return OnAddToMap(map, null, -1);
  }

  public List<string> OnAddToMap(Map map, ILayerContainerEdit groupLayer, int index) {
    //Create map member content - eg layers
    var layer = LayerFactory.Instance.CreateLayer(.....);
    //return list of URIs of layers that were added (to the map or group layer)
    return new List<string>() { layer.URI };
 }

At 3.x, various class methods and properties that used parameters of type Array, or were defined as type Array, now use List or IEnumerable. Additionally, certain methods that returned type Array now return type List or IReadOnlyList. Change affected code as needed. Certain methods that were asynchronous (i.e. have a return type of Task), but did not have an "Async" suffix, now do have an "Async" suffix added (e.g. see LineOfSight class below).

At 3.x, the LineOfSight class "Get" methods now include an additional "Async" suffix to correctly reflect that these methods return type of Task. Additionally, the "Set" methods, at 2.x, returned type void. Now, at 3.x, they are all awaitable and return type of Task and include an "Async" suffix. Note: GetObserver() and SetObserver(camera) are unchanged.

At 3.x, QueryDrawingOutline is now GetDrawingOutline

 //At 2.x, use array
 var rendererDefn1 = new UniqueValueRendererDefinition(new string[] { "field1", "field2" });
 //At 3.x, use list
 var rendererDefn1 = new UniqueValueRendererDefinition(new List<string> { "field1", "field2" });
 
 //At 2.x - various properties use Array
 var colorizerDef = new ColormapColorizerDefinition() {
     Colors = new List<CIMColor>() { ....... }.ToArray(),
     ...

 //At 3.x - corresponding properties changed to use type List
 var colorizerDef = new ColormapColorizerDefinition() {
     Colors = new List<CIMColor>() { ....... },
     ...

 //At 2.x, lineOfSight Get methods do not have 'Async' suffix
 var color = await lineOfSight.GetVisibleColor();
 var color2 = await lineOfSight.GetNotVisibleColor();
 var color3 = await lineOfSight.GetOutOfRangeColor();
 ...
 //At 3.x they do
 var color = await lineOfSight.GetVisibleColorAsync();
 var color2 = await lineOfSight.GetNotVisibleColorAsync();
 var color3 = await lineOfSight.GetOutOfRangeColorAsync();
 ...

 //At 2.x LineOfSight static "Set" methods are not awaitable
 LineOfSight.SetVisibleColor(color);
 LineOfSight.SetNotVisibleColor(color2);
 LineOfSight.SetOutOfRangeColor(color3);

 //At 3.x LineOfSight static "Set" methods are awaitable and have
 //an Async suffix
 await LineOfSight.SetVisibleColorAsync(color);//can await (optional)
 await LineOfSight.SetNotVisibleColorAsync(color2);//can await (optional)
 //not awaited...
 LineOfSight.SetOutOfRangeColorAsync(color3);//can await (optional)
 
 //At 2.x, use "Query...."
 var mask_geom = featLayer.QueryDrawingOutline(oid, mv, DrawingOutlineType.Exact);
 //At 2.x3.0, use "Get...."
 var mask_geom = featLayer.GetDrawingOutline(oid, mv, DrawingOutlineType.Exact);

At 3.x, featureLayer.SetDisplayCacheType(...) has been removed. Addins should use the new featureLayer.SetCacheOptions() method instead.

 // change the layer cache type to maximum age
 QueuedTask.Run(() => {
   //At 2.x
   featureLayer.SetDisplayCacheType(ArcGIS.Core.CIM.DisplayCacheType.MaxAge);
   featureLayer.SetDisplayCacheMaxAge(TimeSpan.FromMinutes(2));

   //At 3.x
   featureLayer.SetCacheOptions(LayerCacheType.MaxAge);
   featureLayer.SetDisplayCacheMaxAge(TimeSpan.FromMinutes(2));  

At 3.x, ArcGIS.Desktop.Mapping.Snapping snap modes have changed. SetSnapModes(...) at 2.x consumed a variable set of zero or more (comma-separated) SnapMode parameters. At 3.x, SetSnapModes consumes an IEnumerable of snap modes.

At 3.x, certain ArcGIS.Desktop.Mapping.SnappingOptions properties have changed. This is mostly for renaming purposes to improve consistency. Note: 2.x snapping options properties GeometricSnapping, SnapRequestType, VisualFeedbackColor, and VisualSnapping have been removed and are no longer supported. Also at 3.x, SnapResult.OID has been renamed to SnapResult.ObjectID.

An example follows:

 // set only Point and Edge snapping modes, clear everything else
 //At 2.x
 ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(SnapMode.Point, SnapMode.Edge);
 ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(); //to clear all snap modes

 //At 3.x
 ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(new List<SnapMode>(){ SnapMode.Point, SnapMode.Edge});
 ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(null); //to clear all snap modes

 //Set snapping options via get/set options
 var snapOptions = ArcGIS.Desktop.Mapping.Snapping.GetOptions(myMap);

 //At 2.x
 snapOptions.SnapToSketchEnabled = true;
 snapOptions.ZToleranceEnabled = true;
 snapOptions.GeometricFeedbackColor = ColorFactory.Instance.RedRGB;

 //At 3.x
 snapOptions.IsSnapToSketchEnabled = true;
 snapOptions.IsZToleranceEnabled = true;
 snapOptions.SnapTipColor = ColorFactory.Instance.RedRGB;

 ArcGIS.Desktop.Mapping.Snapping.SetOptions(snapOptions);

//At 2.x - 
//snapOptions.GeometricSnapping,  snapOptions.SnapRequestType,  snapOptions.VisualFeedbackColor, 
//snapOptions.VisualSnapping are removed at 3.x
      

At 2.x, feature selections against the MapView.GetFeatures,MapView.GetFeaturesEx, MapView.SelectFeatures, and MapView.SelectFeaturesEx methods on the MapView were returned as a generic LINQ collections of type Dictionary<BasicFeatureLayer, List<long>> and Dictionary<Layer, List<long>> respectively. Various methods in the public API at 2.x consumed the returned selection set Dictionary - for example MapView.ZoomTo(IReadOnlyDictionary<BasicFeatureLayer,List<long>>,...), and many of the EditOperation macros such as EditOperation.Move(IEnumerable<KeyValuePair<MapMember,List<long>>>,...), EditOperation.Reshape(IEnumerable<KeyValuePair<MapMember,List<long>>>,...), EditOperation.Rotate(IEnumerable<KeyValuePair<MapMember,List<long>>>,...) etc. (refer to ArcGIS.Desktop.Editing.dll changes for more information.)

At 3.x, the returned LINQ collection has been replaced with a SelectionSet class. The corresponding methods in the public API that consumed the 2.x selected set as a Dictionary now consume the new SelectionSet class. For those addins that were using LINQ to filter the returned selection set Dictionary at 2.x, the SelectionSet class provides a ToDictionary conversion method. ToDictionary() converts the SelectionSet into a LINQ Dictionary collection which can then be manipulated via LINQ same as was possible at 2.x. Some examples follow:

 //At 2.x -
 QueuedTask.Run(()=> {
    var sel_poly = .... ;//Polygon to use for selection

    //zoom to the extent of the retrieved set of features
    MapView.Active.ZoomTo(MapView.Active.GetFeatures(sel_poly));

    //move the selected set of features
    var editOp = new EditOperation() { ..... };
    editOp.Move(MapView.Active.SelectFeatures(sel_poly), 500.0, 500.0);
    editOp.Execute();

    //rotate
    var editOp = new EditOperation() { ..... };
    editOp.Rotate(MapView.Active.SelectFeatures(sel_poly), origin, 35.0);
    editOp.Execute();
   
    //get the geometry of the first selected feature.
    var dict_sel = MapView.Active.GetFeatures(sel_poly);
    var insp = new Inspector();
    insp.Load(dict_sel.Keys.First(), dict_sel.Values.First());
    var selGeom = insp.Shape;

    //Get the list of object ids from SelectFeaturesEx for a particular layer using LINQ
    var sname = "Points of Interest";
    var dict_sel = MapView.Active.SelectFeaturesEx(env);
    var oids1 = dict_sel.Where(kvp => kvp.Key.Name == sname).First().Value;
    //TODO - use the object ids
 });

 //At 3.x
 QueuedTask.Run(() => {
     var sel_poly = .... ;//Polygon to use for selection

    //zoom to the extent of the retrieved set of features - No change
    MapView.Active.ZoomTo(MapView.Active.GetFeatures(sel_poly));

    //move the selected set of features - No change
    var editOp = new EditOperation() { ..... };
    editOp.Move(MapView.Active.SelectFeatures(sel_poly), 500.0, 500.0);
    editOp.Execute();

    //rotate - no change
    var editOp = new EditOperation() { ..... };
    editOp.Rotate(MapView.Active.SelectFeatures(sel_poly), origin, 35.0);
    editOp.Execute();
   
    //get the geometry of the first selected feature. Use ToDictionary()
    //to apply LINQ
    var selSet = MapView.Active.GetFeatures(sel_poly);
    var insp = new Inspector();
    insp.Load(selSet.ToDictionary().Keys.First(), selSet.ToDictionary().Values.First());
    var selGeom = insp.Shape;

    //Get the list of object ids from SelectFeaturesEx for a particular layer. Use ToDictionary()
    //to apply LINQ
    var sname = "Points of Interest";
    var selSet = MapView.Active.SelectFeaturesEx(env);
    var oids1 = selSet.ToDictionary().Where(kvp => kvp.Key.Name == sname).First().Value;
    //TODO - use the object ids

    //Create a selection set from a list of object ids
    //using FromDictionary
    var addToSelection = new Dictionary<MapMember, List<long>>(); 
    addToSelection.Add(us_zips, new List<long> { 1506, 2696, 2246, 1647, 948 }); 
    var sset = SelectionSet.FromDictionary(addToSelection); 
    //TODO - use sset
    //etc
 });

At 3.x, the SelectElements method on MapView has been changed to match the SelectElements definition on IElementContainer which returns void and not IReadOnlyList<Element> as at 2.x. To retrieve the selected elements from executing mapView.SelectElements(...), query either the specified graphics layer or all GraphicsLayers in the TOC, depending on which overload of SelectElements you are/were using:

//At 2.x
//Either - select from all graphics layers
var sel_elems = MapView.Active.SelectElements(
   sel_geom, SelectionCombinationMethod.New, false);
// Or - select just from the specified graphics layer
var gl = mapView.Map.GetLayersAsFlattenedList().OfType<GraphicsLayer>().First();
var sel_elems = MapView.Active.SelectElements(
   gl, sel_geom, SelectionCombinationMethod.New, false);

//At 3.x
MapView.Active.SelectElements(
   sel_geom, SelectionCombinationMethod.New, false);//returns void
//or
var graphicsLayer = mapView.Map.GetLayersAsFlattenedList().OfType<GraphicsLayer>().First();
MapView.Active.SelectElements(
   graphicsLayer, sel_geom, SelectionCombinationMethod.New, false);//returns void

//Get the selected elements
// - if a specific GraphicsLayer was specified then use it
var sel_elems = graphicsLayer.GetSelectedElements();

//No GraphicsLayer was specified, collect selection from all GraphicsLayers
var graphicsLayers = mapView.Map.GetLayersAsFlattenedList().OfType<GraphicsLayer>() ??
				new List<GraphicsLayer>();

var sel_elems = new List<Element>();
foreach (var graphicsLayer in graphicsLayers) {
  sel_elems.AddRange(graphicsLayer.GetSelectedElements());
}

//can also use map.TargetGraphicsLayer
var map = MapView.Active.Map;
var sel_elems = map.TargetGraphicsLayer.GetSelectedElements();

At 3.x, the MapView methods to interact with ArcGIS.Desktop.Mapping.ExploratoryAnalysis now have an "Async" suffix. mapView.RemoveExploratoryAnalysis() and mapView.AddExploratoryAnalysis() are now mapView.RemoveExploratoryAnalysisAsync() and mapView.AddExploratoryAnalysisAsync() respectively.

At 3.x, there are changes to the XML persistence model in use with CIMGenericView and implementations of map pane impersonation using custom map pane view models that derive from TOCMapPaneProviderPane, topic 16597. Custom map panes and impersonation map panes are persisted as CIMGenericView, topic 1481, in the .aprx. Consult the Custom CIMGenericView and ViewXML section of this document for the relevant changes.

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.TaskAssistant.dll

At 3.x, TaskAssistantModule is now internal. Methods previously accessed off TaskAssistantModule at 2.x should be accessed off the new TaskAssistantFactory class at 3.x.

Examples follow:

 //At 2.x
 //Access to tasks and task items via TaskAssistantModule
 var taskItem = Project.Current.GetItems<TaskProjectItem>().FirstOrDefault();
 //open a project task item
 await TaskAssistantModule.OpenTaskItemAsync(taskItem.TaskItemGuid);

 //Get task item information
 string taskFile = @"c:\Tasks\Get Started.esriTasks";
 var taskItemInfo = await TaskAssistantModule.GetTaskItemInfoAsync(taskFile);

 // find the first task in the task item
 var taskInfo = taskItemInfo.GetTasks().FirstOrDefault();
 //open the task
 await TaskAssistantModule.OpenTaskAsync(taskFile, taskInfo.Guid);

 //export
 await TaskAssistantModule.ExportTaskAsync(taskItem.TaskItemGuid, @"c:\temp");//also ImportTaskAsync
 ...
 //Close it and remove from project
 TaskAssistantModule.CloseTaskAsync(taskItem.TaskItemGuid);


 //At 3.x
 //use TaskAssistantFactory
 var taskItem = Project.Current.GetItems<TaskProjectItem>().FirstOrDefault();
 //open a project task item
 await TaskAssistantFactory.Instance.OpenTaskItemAsync(taskItem.TaskItemGuid);

 //Get task item information
 string taskFile = @"c:\Tasks\Get Started.esriTasks";
 var taskItemInfo = await TaskAssistantFactory.Instance.GetTaskItemInfoAsync(taskFile);

 // find the first task in the task item
 var taskInfo = taskItemInfo.GetTasks().FirstOrDefault();
 //open the task - notice -OpenTaskFileAsync-
 await TaskAssistantFactory.Instance.OpenTaskFileAsync(taskFile, taskInfo.Guid);

 //export - ExportTaskItemAsync
 await TaskAssistantFactory.Instance.ExportTaskItemAsync(taskItem.TaskItemGuid, @"c:\temp");
 //also ImportTaskFileAsync ...
 ...
 //Close it and remove from project - notice -CloseTaskItemAsync
 TaskAssistantFactory.Instance.CloseTaskItemAsync(taskItem.TaskItemGuid);

Consult What's New in the API reference for the complete list of API changes at 3.0.

ArcGIS.Desktop.Workflow.dll

Consult What's New in the API reference for the complete list of API changes at 3.0.

CIM Persistence

At 3.x, xml serialization methods for CIM objects have been removed. Addins, at 2.x, using XML serialization and deserialization via the cimObject.ToXml() and CIMObject.FromXml() method pair will need to switch to using JSON at 3.x. The switch in the CIM persistence model to json provides for a more compact serialization format reducing the size of the Pro .aprx and is faster than xml when it comes to serialize/deserialize performance

To persist (or serialize) CIM objects at 3.x developers should now use the overriden cimObject.ToJson() instance method, topic 24486, on the relevant derived CIM class. To deserialize CIM json, addins should use the static derived CIMObject.FromJson(json_string) class method, topic 74865 (on the relevant CIM class).

Below is an example of CIM persistence at 3.x using json:

 //assume we have a feature layer ....
 var map = MapView.Active.Map;
 var fl = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().First();
			
 QueuedTask.Run(() => {
    //get a cim definition to be serialized - eg the renderer definition
    var simple_renderer = fl.GetRenderer() as CIMSimpleRenderer;

    //To persist serialized state at 3.x, use json
    var renderer_json = original_object.ToJson();//No ToXml() at 3.x
    
    //deserialize json to get back to original object
    var simple_renderer2 = CIMSimpleRenderer.FromJson(renderer_json);//No FromXml() at 3.x

     //Apply to the layer
    fl.SetRenderer(original_object2);
    ...

Geometry instance serialization to/and from xml at 3.x is still supported.

Converting CIM XML Previously Persisted at 2.x

Addins using persisted xml CIM strings previously stored in files, custom databases, or other storage locations that were serialized at 2.x, should use the ArcGIS.Core.CIM.XmlUtils.UpgradeAndDeserializeCIMObject(xml) method, topic 75062, to do a one-way conversion of the persisted 2.x CIM object to a 3.x compliant object. UpgradeAndDeserializeCIMObject will do both the necessary deserialization from xml as well as any necessary upgrade conversion of the persisted CIM object to its proper 3.x version.

Note: there is no backwards compatibility meaning 2.x CIM xml converted to a 3.x CIM object cannot be back-ported to its original 2.x format. Deserialization and upgrade to 3.x is one-way.

The following example shows how to use UpgradeAndDeserializeCIMObject to deserialize and upgrade 2.x CIM xml:

using ArcGIS.Core.CIM;
...

 //assume we have a feature layer ....
 var map = MapView.Active.Map;
 var fl = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().First();
			
 QueuedTask.Run(() => {
    //2.x CIM xml retrieved from a file, proprietary database, etc.
    var simple_renderer_xml = ... ;
    
    //use XmlUtils.UpgradeAndDeserializeCIMObject(xml) to hydrate the CIM object 
    //at 3.x from 2.x xml - it will do a one-way conversion and upgrade to a 3.x CIMObject
    //No CIMObject.FromXml() at 3.x
    var simple_renderer = (CIMSimpleRenderer)XmlUtils.UpgradeAndDeserializeCIMObject(simple_renderer_xml);

    //Apply to the layer
    fl.SetRenderer(simple_renderer);

    //TODO - change renderer properties, etc. 
    ...
    //persist renderer in json to save back in proprietary database, file on disk, etc.
    var renderer_json = fl.GetRenderer().ToJson();
    //From this point on, addin uses json for CIM persistence workflows

Custom CIMGenericView and ViewXML

At 3.x, the view.ViewXML string property has been removed. At 2.x addins could persist custom view state into the view.ViewXML property (as well as a view.ViewProperties dictionary). . Custom views must switch to using the CIMGenericView view.ViewProperties dictionary to persist custom content at 3.x. Custom content written into the ViewProperties dictionary can be persisted into the aprx whenever the project is saved. Persistence is implemented via a callback to the overridden public override CIMView ViewState property, topic 9234, on the view's view model (all custom view view models derive from the ArcGIS.Desktop.Core.ViewStatePane base class). Note: By default view.ViewProperties is null. Instantiate as needed before use.

Examples follow:

Change all occurences of view.ViewXML to CIMGenericView view.ViewProperties[...] (with an appropriate key) in your custom views:

 //At 2.x
 view.ViewXML = new XDocument(new XElement("Root",
               new XElement("custom", "custom value"))).ToString(
                                                    SaveOptions.DisableFormatting);

 //At 3.x
 //Use CIMGenericView ViewProperties Dictionary
 if (view.ViewProperties == null)
   view.ViewProperties = new Dictionary<string, object>();
 view.ViewProperties["My_Custom_Content"] = "Custom content -xml or otherwise";

In this example, a custom view was using view.ViewXML in an (internal) static "factory" Create method that is now replaced with the view.ViewProperties dictionary:

 internal class CustomPaneViewModel : ViewStatePane, IContentsProvider, IContentsControl {
   private const string _viewPaneID = "....";
   ...
   public CustomPaneViewModel(CIMView view) : base(view) {
     ...
   /// <summary>
   /// Create a new instance of the pane.
   /// </summary>
   internal static CustomPaneViewModel Create() {
      var view = new CIMGenericView();
      view.ViewType = _viewPaneID;
      //At 2.x
      // view.ViewXML = 
      //   "<Root><LastChanged>" + DateTime.Now.ToString("HH:mm:ss tt") + "</LastChanged></Root>";

      //At 3.x, Use ViewProperties
      view.ViewProperties = new Dictionary<string, object>();
      view.ViewProperties["LastChanged"] = DateTime.Now.ToString("HH:mm:ss tt");

      return FrameworkApplication.Panes.Create(
          _viewPaneID, new object[] { view }) as CustomPaneViewModel;
  }
  ...

 /// <summary>
 /// Button implementation to create a new instance of the pane and activate it.
 /// </summary>
 internal class CustomPane_OpenButton : Button {
   protected override void OnClick() {
     CustomPaneViewModel.Create();
   }
 }

In this example, a custom view has switched to using ViewProperties in its ViewState property callback (accessed when the project is being saved). The content written into view.ViewProperties will be peristed in the aprx:

internal class AcmeCustomMapPaneViewModel : TOCMapPaneProviderPane {
  private const string _viewPaneID = "....";
  private string _customValue = "....";
  ...

  /// <summary>
  /// Must be overridden in child classes used to persist the state of the view to the CIM.
  /// </summary>
  public override CIMView ViewState {
      get {
        //If we are here, the project is being saved
        _cimView.InstanceID = (int)InstanceID;
       //At 2.x
       // _cimView.ViewXML = 
       //   "<Root><LastChanged>" + DateTime.Now.ToString("HH:mm:ss tt") + "</LastChanged></Root>";

       //At 3.x, Use ViewProperties
       if (((CIMGenericView)_cimView).ViewProperties == null)
          ((CIMGenericView)_cimView).ViewProperties = new Dictionary<string, object>();

       ((CIMGenericView)_cimView).ViewProperties["LastChanged"] = DateTime.Now.ToString("HH:mm:ss tt");

       //Store anything else that the custom view requires
       //Note: this code works at 2.x as well
       ((CIMGenericView)_cimView).ViewProperties["foo"] = "bar";
       ((CIMGenericView)_cimView).ViewProperties["custom"] = _customValue;
       //can also include serialized content
       ((CIMGenericView)_cimView).ViewProperties["self"] = _cimView.ToJson();
       //etc
       return _cimView;
    }

If a 2.x project is being opened with custom view state previously persisted in the view.ViewXML, it can be retrieved from the view.ViewProperties dictionary using the reserved key "ViewXML" (assuming that the custom view addin has been migrated to 3.x). Note: Custom views at 2.x, migrated to 3.x, that were already using the view.ViewProperties dictionary (rather than view.ViewXML) do not need to make any changes (beyond whatever other changes are needed for migration).

This example illustrates a "before and after" implementation of a custom view using view.ViewXML at 2.x and switching to use of view.ViewProperties["ViewXML"] at 3.x. Notice how the 3.x version of the custom view checks for the presence of view.ViewProperties["ViewXML"].

Recall, a view.ViewProperties["ViewXML"] value will be added to the view.ViewProperties dictionary if a 2.x aprx is being opened in 3.x and it contains custom view content (for your view) previously persisted via the view.ViewXML property.

//At 3.x. view.ViewXML has been removed. Use view.ViewProperties for persistence
internal class AcmeCustomMapPaneViewModel : TOCMapPaneProviderPane {
  private const string _viewPaneID = "....";
  private string _lastChangeToProperties = "";
  private string _customContent = "";
  private string _bar = "";
  /// <summary>
  /// Must be overridden in child classes used to persist the state of the view to the CIM.
  /// </summary>
  public override CIMView ViewState {
      get {
        //If we are here, the project is being saved
        _cimView.InstanceID = (int)InstanceID;

         //At 3.x - use view.ViewProperties. There is no ViewXML
        _lastChangeToProperties = DateTime.Now.ToString("HH:mm:ss tt");

        if (((CIMGenericView)_cimView).ViewProperties == null)
          ((CIMGenericView)_cimView).ViewProperties = new Dictionary<string, object>();

        ((CIMGenericView)_cimView).ViewProperties["LastChanged"] = 
                                      _lastChangeToProperties ;
        ((CIMGenericView)_cimView).ViewProperties["foo"] = _bar;
        ((CIMGenericView)_cimView).ViewProperties["custom"] = _customValue;
        //can also include serialized content
        ((CIMGenericView)_cimView).ViewProperties["self"] = _cimView.ToJson();
        //etc
        return _cimView;
      }
    }
 
 //read in any of our previously persisted state/content
 protected async override Task InitializeAsync() {
   if (((CIMGenericView)_cimView).ViewProperties == null)
        return;

   var uri = ((CIMGenericView)_cimView).ViewProperties["MAPURI"] as string;

   //use ViewProperties same as in 2.x
   if (((CIMGenericView)_cimView).ViewProperties.ContainsKey("LastChanged"))
     _lastSave = ((CIMGenericView)_cimView).ViewProperties["LastChanged"] as string;

   if (((CIMGenericView)_cimView).ViewProperties.ContainsKey("foo"))
     _bar = ((CIMGenericView)_cimView).ViewProperties["foo"] as string;

   if (((CIMGenericView)_cimView).ViewProperties.ContainsKey("custom"))
     ... etc, etc,...

   //Now check if this a 2.x project being opened in 3.0 that had previously used view.ViewXML.... 
   //If so, at 3.x, it will have a "special" "ViewXML" key that will have been added to the
   //ViewProperties containing the legacy content...
   //...
   if (((CIMGenericView)_cimView).ViewProperties.ContainsKey("ViewXML")) { //Legacy content
       var previous_custom_content = ((CIMGenericView)_cimView).ViewProperties["ViewXML"] as string;
       //TODO Process the custom content that was persisted at 2.x
       
       string customContentFromViewXML = .... ;
       //Ok to delete "ViewXML" once its been read (but not required)
       ((CIMGenericView)_cimView).ViewProperties.Remove("ViewXML");
    }

Once 2.x content has been read from ViewProperties["ViewXML"], the key can be safely removed if desired.

Map, Layout, and Layer Files and Packages

Map, Layout, and Layer (.mapx, .pagx, .lyrx) and packages (.mpkx, .lpkx), created at 2.x, can be used with the public API at 3.0 with no changes. They will automatically be converted to 3.0 format when they are consumed by the API.

For example:

 //At 3.x, consume 2.x files and packages in the public API
 QueuedTask.Run(()=> {
    //Create a map using a 2.x map file or package
    var mapx_path = @"E:\Data\SDK\Migration\Crimes_Map_29.mapx";//or package
    MapFactory.Instance.CreateMap(new Uri(mapx_path));

    //Create a layout using a 2.x layout file
    var lytx_path = @"E:\Data\SDK\Migration\Crimes_Layout_29.pagx";
    var lytx_item = ItemFactory.Instance.Create(lytx_path) as IProjectMultiItem;
    Project.Current.ImportItem(lytx_item, false, true);
 });

In this example, the addin is reading in a 2.x layer file, or layer package, that is being used to create a new layer via the FeatureLayerCreationParams class, topic 26495:

  var map = MapView.Active.Map;

  QueuedTask.Run(() => {
    //Use 2.x Layer files (.lyrx) or packages (.lpkx) at 3.x
    var layer_file_29 = @"E:\Data\SDK\Migration\Crimes_29.lyrx";//or .lpkx

    var fl_params = new FeatureLayerCreationParams(new Uri(layer_file_29)) {
      IsVisible = true,
      Name = "From_Layer_File",
      MapMemberPosition = MapMemberPosition.AddToTop
    };

    //Create the new layer. 
    //2.x file/package is automatically converted to 3.x
    LayerFactory.Instance.CreateLayer<FeatureLayer>(fl_params, map);
    ...

In the last example, an addin is using a 2.x layer file to create a LayerDocument, topic 26521. The LayerDocument is being used to customize the symbology of the resulting layer.

  var map = MapView.Active.Map;

  QueuedTask.Run(() => {
    //Use 2.x Layer files (.lyrx) with LayerDocument class at 3.x
    //Note: LayerDocument class does not support use with .lpkx files
    var layer_file_29 = @"E:\Data\SDK\Migration\Crimes_29.lyrx";

   //Create the 3.x layer document with the 2.x layer file
   //The 2.x layer file is automatically converted
   var layerDoc = new LayerDocument(layer_file_29);

   //Get the CIMLayerDocument from the layer document and
   //customize the layer doc properties - eg visibility and renderer
   var cimLayerDoc = layerDoc.GetCIMLayerDocument();

   var layerDefinitions = cimLayerDoc.LayerDefinitions;
   var layerDef = layerDefinitions[0] as CIMFeatureLayer;
   layerDef.Visibility = false;
   layerDef.Renderer = new CIMSimpleRenderer() {
      Symbol = SymbolFactory.Instance.ConstructPointSymbol(
                   CIMColor.CreateRGBColor(200, 0, 200)).MakeSymbolReference()
   };

  //Use the modified CIM layerdoc to create a layer and add it to the map
  var fl_params = new FeatureLayerCreationParams(cimLayerDoc);
  LayerFactory.Instance.CreateLayer<FeatureLayer>(fl_params2, map);
  ...

Map, Layout, and Layer URIs

The URI format on Map, Layout, and Layer models at 3.x has changed from the general format of CIMPATH=map/<object identifier or name>.xml for maps and layers and CIMPATH=layout/<object identifier or name>.xml for layouts to CIMPATH=map/<object identifier or name>.json and CIMPATH=layout/<object identifier or name>.json respectively. Notice the change in suffix from ".xml" at 2.x to ".json" at 3.x. Addin code that consumes or references a URI will not need to change unless it was depending on the URI string suffix of ".xml" directly for some reason.

Additional Migration Notes

There are a number of different implementation patterns and content types that can be implemented, or in use, within 2.x addins. Migration considerations for these patterns are further discussed in this section.

Custom Project Properties

Addins can store custom properties within ArcGIS Pro .aprx project files. Custom properties are accessed when the specific project containing the custom properties is opened and the addin that persisted the custom project properties has been loaded. To access custom project properties, as long as the addin has been migrated to 3.x no further action is required. When a 2.x project file is opened in 3.x, it will also include any custom addin properties. Saving the project file (to 3.x) will persist the custom project properties into the 3.x version (of the aprx).

For more information on custom project properties, refer to ProGuide, Custom Settings

Custom Application Properties

Addins can store custom properties within the application user.config file. These are accessed via the Microsoft auto-generated Settings class, (derived from System.Configuration.ApplicationSettingsBase). To migrate your custom application properties to the ArcGIS Pro 3.x user.config, addin developers must:

  • Migrate the addin to 3.x
  • Define a boolean property in your addin custom settings called UpgradeNeeded with a default value if true.

The name of the boolean property is arbitrary, but the usual convention is to call it UpgradeNeeded. In your derived settings class constructor, check the value of the UpgradeNeeded property. If it is true, call the base class Upgrade method which will migrate your custom settings into the Pro 3.x user.config. The code in your settings file constructor should be similar to:

public Settings() {
   if (UpgradeRequired) {
      UpgradeRequired = false;//store a value of false in the user.config
      Save();//save the settings
      Upgrade();//base class Upgrade - this does the migration
   }
 }

The procedure is further detailed in ProGuide Custom Settings. The procedure is also detailed in this stack overflow post.

Embedded Python Toolbox

The addin migration tool will migrate the addin as well as the custom toolbox content to 3.x. No further action is required. However, as previouly mentioned earlier in this document, the special content tag "AddinContent" is no longer supported at 3.x. The migration tool converts all of the toolbox content previously marked as AddinContent at 2.x to the built-in type "Content" (see below). The built-in Content type behaves in exactly the same way as did AddinContent.

toolbox-after.png

Run the toolbox addin.

toolbox-running-3.0.png

Open the converted .csproj or .vbproj, to view the changed the toolbox content types:

<!-- Before migration-->
<ItemGroup>
    <AddInContent Include="Toolboxes\arcpy\DeepThought.py" />
    <AddInContent Include="Toolboxes\toolboxes\answer.py" />
    <AddInContent Include="Toolboxes\toolboxes\sixbynine.py" />
    <AddInContent Include="Toolboxes\help\gp\Answer_deepthought.xml" />
    <AddInContent Include="Toolboxes\help\gp\deepthought_toolbox.xml" />
    <AddInContent Include="Toolboxes\help\gp\messages\messages.xml" />
    <AddInContent Include="Toolboxes\help\gp\toolboxes\DeepThought.xml" />
    <AddInContent Include="Toolboxes\toolboxes\DeepThought.tbx" />
  </ItemGroup>

<!-- After migration-->
<ItemGroup>
    <Content Include="Toolboxes\arcpy\DeepThought.py" />
    <Content Include="Toolboxes\toolboxes\answer.py" />
    <Content Include="Toolboxes\toolboxes\sixbynine.py" />
    <Content Include="Toolboxes\help\gp\Answer_deepthought.xml" />
    <Content Include="Toolboxes\help\gp\deepthought_toolbox.xml" />
    <Content Include="Toolboxes\help\gp\messages\messages.xml" />
    <Content Include="Toolboxes\help\gp\toolboxes\DeepThought.xml" />
    <Content Include="Toolboxes\toolboxes\DeepThought.tbx" />
  </ItemGroup>

Consult the ProGuide Content and Image Resources, Python Toolboxes for more information on custom content, embedding python toolboxes.

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