ProConcepts 3.0 Migration Guide,Pro2.8升级ArcGIS Pro3.0
liebian365 2024-11-18 14:20 4 浏览 0 评论
来自:https://github.com/Esri/arcgis-pro-sdk/wiki/ProConcepts-3.0-Migration-Guide
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.
Language: C#
Subject: Framework
Contributor: ArcGIS Pro SDK Team <arcgisprosdk@esri.com>
Organization: Esri, http://www.esri.com
Date: 6/17/2022
ArcGIS Pro: 3.0
Visual Studio: 2022
In this topic
- Overview Pro Document Version Change Forwards Compatibility Registering addins Add-in and Configuration Does Not Load Build Server use of ArcGISSignAddIn.exe
- Migrating to Pro 3.0 Install Pro and the Pro SDK for 3.0 Step 1 Conversion Conversion Notes Target Framework Config.daml desktopVersion and Config.xml version Attributes Config.xml language Attribute AddinContent Target "AfterBuild" Pro SDK Nuget Reference Other Custom Content XAML Resources Marked As Page 3rd Party References and Non Esri DLLs NotUsed Files Copy Local Not Working Step 2 Recompile and Fix API Breaking Changes .NET 6 Microsoft Windows Compatibility Pack Deprecated Types and Members 64-bit Object IDs Additional API Breaking Changes Additional Resources
- Breaking Changes By Assembly ArcGIS.Core.dll ArcGIS.CoreHost.dll ArcGIS.Desktop.Catalog.dll ArcGIS.Desktop.Core.dll ArcGIS.Desktop.DataReviewer.dll ArcGIS.Desktop.Editing.dll ArcGIS.Desktop.Extensions.dll ArcGIS.Desktop.Framework.dll ArcGIS.Desktop.Geoprocessing.dll Esri.ArcGIS.ItemIndex.dll ArcGIS.Desktop.Layout.dll ArcGIS.Desktop.Mapping.dll ArcGIS.Desktop.TaskAssistant.dll ArcGIS.Desktop.Workflow.dll
- CIM Persistence Converting CIM Xml Previously Persisted at 2.x Custom CIMGenericView and ViewXML Map, Layout, and Layer Files and Packages Map, Layout, and Layer URIs
- Additional Migration Notes Custom Project Properties Custom Application Properties Embedded Python Toolbox
Overview
At 3.0, ArcGIS Pro has 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
- The public APIs (of Pro) are subject to change
- The major version increment of Pro is changing from 2.x to 3.0.
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.) has 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.0 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.0 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.
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.0. When you run Pro, previously installed addins, configurations, and plugin datasources from a 2.x release will not load in Pro 3.0. 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 before they can be loaded. The same is true for configurations, plugin datasources, and core host applications*.
*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.0, 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.0 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:
- Addins, configurations, plugin datasources, and core host applications must be converted from .NET Framework 4.x to .NET 6
- Addins, configurations, plugin datasources, and core host applications must be recompiled against 3.0 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.0
To migrate your addins at 3.0 following the procedure in this document, it is assumed that you have installed the 3.0 Pro SDK. Minimum requirements for the Pro SDK at 3.0 are Visual Studio 2022 and .NET 6. Consult the ProGuide Installation and Upgrade document for detailed installation instructions and minimum requirements.
Step 1 Conversion
Migration to Pro 3.0 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.0 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. This will add the utility to the Visual Studio 2022 project context menu (alongside the other Pro SDK context menu options).
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:
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.0
- For plugins, the version attribute will have been changed to 3.0
- 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.0 Nuget. Refer to ProGuide ArcGIS Pro Extensions Nuget for more information on the 3.0 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.0.
*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:
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.0, otherwise these changes must be made by hand.
Refer to Pro Concepts Advanced Topics, Add-in Versioning for more information on versioning.
Config.xml Language Attribute
At 3.0, the <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.0 -->
<AddIn language="CLR:PluginDS" library="AcmePluginDatasource.dll" namespace="AcmePluginDatasource">
<ArcGISPro>
...
AddinContent
At 3.0, 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.
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.0 -->
<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*.
* At the time of writing this document, 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.0, 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".
When adding a new Resource Dictionary at 3.0 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.0 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:
- 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.
- Classes, methods, and properties previously deprecated at a 2.x release are now deleted at 3.0
- 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.
- 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.0. 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 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, 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.0 - 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. 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.0.
The What's New for Developers at 3.0 in the API reference contains the complete listing of all API changes.
Breaking Changes By Assembly
This section provides an overview of the main breaking changes, per assembly, to assist you in 3.0 addin migration. Consult What's New in the API reference for the complete list of API changes.
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.0 CIM object instance, addins should use ArcGIS.Core.CIM.XmlUtils.UpgradeAndDeserializeCIMObject(xml), topic 75062 which has been 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.0
//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 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 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 has been 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 has been 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.0 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.0
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.0
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.0
using ArcGIS.Core.Geometry;
using ArcGIS.Core.Geometry.Exceptions
try {
...
} catch(GeometryException ge) {//or use explicit reference
//ArcGIS.Core.Data.Exceptions.GeometryException
At 3.0, 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 have been 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.0 - 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, has been 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.
ArcGIS.CoreHost.dll
Consult What's New in the API reference for the complete list of API changes.
ArcGIS.Desktop.Catalog.dll
At 3.0, the return value from ArcGIS.Desktop.Catalog.OpenItemDialog.Items has been changed from IEnumerable<Item> to IList<Item>.
ArcGIS.Desktop.Catalog.ItemFilters properties have been camel cased. So, for example:
// At 2.x At 3.0
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.
ArcGIS.Desktop.Core.dll
Cef and use of Cef sharp was deprecated at 2.9. Now, at 3.0, Cef is no longer supported. The method, ProApp.InitializeCef() has been 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.
ArcGIS.Desktop.DataReviewer.dll
Consult What's New in the API reference for the complete list of API changes.
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) has been 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.0.
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.0
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.0, the Inspector will always be instantiated allowing features or rows to be updated.
//at 2.x
var insp = new Inspector(true | false);
//At 3.0
var insp = new Inspector();//isFeatureEditing = true is just assumed.
At 3.0, the EditOperation.Create overloads now 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 2.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(#34;{featLayer.Name}: new feature: {newFeatureID}");
//At 3.0
long newFeatureID = -1;
var rowToken = editOp.Create(featLayer, poly);
if (editOp.Execute()) {
//operation executed successfully
long newFeatureID = (long)rowToken.ObjectID;
WriteToLog(#34;{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. 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.0, 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 has been changed to editOperation.IsCanceled .
At 3.0, editOperation.Duplicate(...) is removed. Addins using editOperation.Duplicate at 2.x, should use either a copy + move or a create + move edit operation method combination at 3.0.
var editOp = new EditOperation();
editOp.Name = "Duplicate Features";
//At 2.x
editOp.Duplicate(featureLayer, oid, 500.0, 500.0, 0.0);
//At 3.0, 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(...) have been replaced with an overload of editOperation.Transform(...), topic 9562, that consumes a TransformMethod. At 3.0, 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.0
//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 have been 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.0, 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 has been 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.0 -->
<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.
ArcGIS.Desktop.Framework.dll
At 3.0, the ArcGIS.Desktop.Framework.Controls.ChromiumWebBrowser control, which was deprecated at 2.9, has been removed. Addins should use the ArcGIS.Desktop.Framework.Controls.WebViewBrowser control instead.
At 3.0, the behavior of the application title and name has changed. At 3.0, 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.0, only title will show.
var date_time = System.DateTime.Now.ToString("g");
FrameworkApplication.Name = date_time;
FrameworkApplication.SubTitle = "MyMap";
FrameworkApplication.Title = Project.Current.Name;
//At 3.0, 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
//At 3.0 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
At 3.0, 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.0, 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.0, 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.0, 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.0
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.
ArcGIS.Desktop.Geoprocessing.dll
At 3.0, ArcGIS.Desktop.Geoprocessing.HistoryProjectItem, deprecated at 2.1, has been removed.
Consult What's New in the API reference for the complete list of API changes.
Esri.ArcGIS.ItemIndex.dll
Consult What's New in the API reference for the complete list of API changes.
ArcGIS.Desktop.Layout.dll
At 3.0, LayoutElementFactory has now been 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 have been 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.0, 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.0, 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.0, 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.0 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.0 - 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.0, 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.0, a list of elements to be grouped must be provided
ElementFactory.Instance.CreateGroupElement(container, new List<Element>() { titleElm });
At 3.0, 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.0, 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.0.
//Layout events
// At 2.x -- At 3.0
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.0
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.0 use:
ElementEvent, hint: ElementEventArgs.ElementEventHint.SelectionChanged
At 3.0, 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.
ArcGIS.Desktop.Mapping.dll
At 3.0, LayerFactory create layer macros have been 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.0.
At 3.0, 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.0 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.0
//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.0, 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.0, 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.0 CreateGroupLayer is unchanged
LayerFactory.Instance.CreateGroupLayer(map, 0, "Group 1");//same as was at 2.x
At 3.0, 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.0.
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),
container);
//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.0
//use a local path uri
var table = StandaloneTableFactory.Instance.CreateStandaloneTable(
new Uri(@"C:\Temp\Data\SDK.gdb\EarthquakeDamage", UriKind.Absolute), map);
//At 3.0
//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.0, 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.0, 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.0, using DefinitionQuery
var defQuery = new DefinitionQuery() {
Name = "California",
WhereClause = "STATE_NAME = 'California'"
};
Here is the 2x and 3.0 declarations of ITableDefinitionQueries for comparison. Note how the 3.0 flavor is DefinitionQuery based than on CIMDefinitionFilter as 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.0 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.0, 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.0, 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.0, VoxelLayer has been changed. At 3.0, 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.0 (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.0, 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.0, 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 = #34;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.0,
//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 = #34;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.0, 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.0
var variable = voxel.SelectedVariableProfile;
var min = variable.Statistics.MinimumValue;
var max = variable.Statistics.MaximumValue;
At 3.0, 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.0
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.0, ElevationSurface and ElevationSource are removed and Elevation surfaces are no longer retrieved from a scene as CIMMapElevationSurface instances. At 3.0, 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.0, 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.0 use ElevationSurfaceLayer
var surfaceLayers = map.GetElevationSurfaceLayers();
var surfaceLayer = map.GetGroundElevationSurfaceLayer();
//At 3.0 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.0, 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.0, 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.0, 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.0 - 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.0, 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.0. Also at 3.0, GetDataSourceType() and GetDataConnection() can be accessed off the layer directly.
At 3.0, PointCloudSceneLayer QueryAvailableClassCodesAndLabels, QueryAvailableClassFlagsAndLabels, and QueryAvailablePointCloudRendererFields methods are renamed to GetAvailableClassCodesAndLabels, GetAvailableClassFlagsAndLabels, and GetAvailablePointCloudRendererFields respectively.
At 3.0, 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.0 - 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.0
var classCodesAndLabels = pcsl.GetAvailableClassCodesAndLabels();
var classFlagsAndLabels = pcsl.GetAvailableClassFlagsAndLabels();
var flds = pcsl.GetAvailablePointCloudRendererFields(PointCloudRendererType.UniqueValueRenderer);
//At 3.0 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.0
bsl.UpdateFilter(filter1);
At 3.0, 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.0 definition of IMappableItem. At 3.0, 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.0 -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.0, 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.0, 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.0, they are all awaitable and return type of Task and include an "Async" suffix. Note: GetObserver() and SetObserver(camera) are unchanged.
At 3.0, QueryDrawingOutline is now GetDrawingOutline
//At 2.x, use array
var rendererDefn1 = new UniqueValueRendererDefinition(new string[] { "field1", "field2" });
//At 3.0, 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.0 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.0, 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.0
featureLayer.SetCacheOptions(LayerCacheType.MaxAge);
featureLayer.SetDisplayCacheMaxAge(TimeSpan.FromMinutes(2));
At 3.0, 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.0, SetSnapModes consumes an IEnumerable of snap modes.
At 3.0, 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.0, 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.0
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.0
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.0
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.0, 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.0
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.0, 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.0
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.0, 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.0, 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.
ArcGIS.Desktop.TaskAssistant.dll
At 3.0, TaskAssistantModule is now internal. Methods previously accessed off TaskAssistantModule at 2.x should be accessed off the new TaskAssistantFactory class at 3.0.
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.0
//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.
ArcGIS.Desktop.Workflow.dll
Consult What's New in the API reference for the complete list of API changes.
CIM Persistence
At 3.0, 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.0. 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.0 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.0 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.0, use json
var renderer_json = original_object.ToJson();//No ToXml() at 3.0
//deserialize json to get back to original object
var simple_renderer2 = CIMSimpleRenderer.FromJson(renderer_json);//No FromXml() at 3.0
//Apply to the layer
fl.SetRenderer(original_object2);
...
Geometry instance serialization to/and from xml at 3.0 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.0 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.0 version.
Note: there is no backwards compatibility meaning 2.x CIM xml converted to a 3.0 CIM object cannot be back-ported to its original 2.x format. Deserialization and upgrade to 3.0 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.0 from 2.x xml - it will do a one-way conversion and upgrade to a 3.0 CIMObject
//No CIMObject.FromXml() at 3.0
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.0. 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.0
//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.0, 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.0, 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.0). Note: Custom views at 2.x, migrated to 3.0, 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.0. Notice how the 3.0 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.0 and it contains custom view content (for your view) previously persisted via the view.ViewXML property.
//At 3.0. 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.0 - 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.0, 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.0, 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.0
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.0
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.0
//Note: LayerDocument class does not support use with .lpkx files
var layer_file_29 = @"E:\Data\SDK\Migration\Crimes_29.lyrx";
//Create the 3.0 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.0 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.0. 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.0 no further action is required. When a 2.x project file is opened in 3.0, it will also include any custom addin properties. Saving the project file (to 3.0) will persist the custom project properties into the 3.0 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.0 user.config, addin developers must:
- Migrate the addin to 3.0
- 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.0 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.0. No further action is required. However, as previouly mentioned earlier in this document, the special content tag "AddinContent" is no longer supported at 3.0. 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.
Run the toolbox addin.
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.
相关推荐
- 快递查询教程,批量查询物流,一键管理快递
-
作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...
- 一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递
-
对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...
- 快递查询单号查询,怎么查物流到哪了
-
输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...
- 3分钟查询物流,教你一键批量查询全部物流信息
-
很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...
- 快递单号查询,一次性查询全部物流信息
-
现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...
- 快递查询工具,批量查询多个快递快递单号的物流状态、签收时间
-
最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...
- 快递查询软件,自动识别查询快递单号查询方法
-
当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...
- 教你怎样查询快递查询单号并保存物流信息
-
商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...
- 简单几步骤查询所有快递物流信息
-
在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...
- 物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号
-
最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...
- 连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息
-
快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...
- 快递查询教程,快递单号查询,筛选更新量为1的单号
-
最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...
- 掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析
-
在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...
- 从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息
-
在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...
- 物流单号查询,在哪里查询快递
-
如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)