|
POST
|
Line 38 and 40 deal add to an existing dictionary entry (if ContainsKey is true) or creates a new dictionary entry. As for your second QueryTable simply swap layer1 and layer2 and the corresponding field names.
... View more
09-14-2021
09:16 AM
|
0
|
1
|
3512
|
|
POST
|
The ModifyNewlyAddedFeatures community sample has a 'Modify Monitor' dockpane that has a similar status text box as you show in your post. This status text (implemented as a simple WPF TextBox control) is updated from background or GUI threads. Here is the method: arcgis-pro-sdk-community-samples/ModifyMonitorViewModel.cs at master · Esri/arcgis-pro-sdk-community-samples (github.com) If you open this sample in Visual Studio you can follow the logic required to call this method from anywhere in you add-in.
... View more
09-13-2021
04:35 PM
|
1
|
0
|
1360
|
|
POST
|
The concepts for QueryTable can be found here: ProConcepts Geodatabase · ArcGIS/arcgis-pro-sdk Wiki (github.com) As to whether you should use a Dictionary object or open a QueryTable my general rule is to use a dictionary for small datasets because the code is simple and maintenable, however, dictionaries don't work well when you are dealing with large datasets, in that case it will be more efficient to match-up your records on the service level and then only deal with the matched records. In the following sample matches records from two feature classes using a QueryTable and then collects the result in a dictionary. This is the output: And the code snippet: protected override async void OnClick()
{
try
{
var layer1Name = "TestPoints";
var layer2Name = "TestLines";
var layer1JoinColumnName = "CodePoint";
var layer2JoinColumnName = "Code";
var result = await QueuedTask.Run(() =>
{
var defGdb = new FileGeodatabaseConnectionPath(new Uri(Project.Current.DefaultGeodatabasePath));
var dicResult = new Dictionary<long, List <(long, long)>>();
using (Geodatabase geodatabase = new Geodatabase(defGdb))
{
QueryDef queryDef = new QueryDef
{
Tables = $@"{layer1Name} JOIN {layer2Name} on {layer1Name}.{layer1JoinColumnName} = {layer2Name}.{layer2JoinColumnName}",
SubFields = $@"{layer1Name}.{layer1JoinColumnName}, {layer1Name}.TheInteger, {layer2Name}.TheInteger",
};
QueryTableDescription queryTableDescription = new QueryTableDescription(queryDef)
{
Name = "JoinedPointLine",
PrimaryKeys = geodatabase.GetSQLSyntax().QualifyColumnName(layer1Name, layer1JoinColumnName)
};
Table queryTable = geodatabase.OpenQueryTable(queryTableDescription);
using (RowCursor cursor = queryTable.Search())
{
while (cursor.MoveNext())
{
using (var row = cursor.Current)
{
var key = Convert.ToInt64(row[0]);
var lyr1Int = Convert.ToInt64(row[1]);
var lyr2Int = Convert.ToInt64(row[2]);
if (dicResult.ContainsKey(key))
dicResult[key].Add ((lyr1Int, lyr2Int));
else
dicResult.Add(key, new List<(long, long)> { (lyr1Int, lyr2Int) });
}
}
}
}
return dicResult;
});
var sb = new StringBuilder();
foreach (var key in result.Keys)
{
sb.AppendLine($@" code: {key}");
foreach (var item in result[key])
{
sb.AppendLine($@" {item.Item1}, {item.Item2}");
}
}
MessageBox.Show($@"Results: {result.Keys.Count}{System.Environment.NewLine}{sb.ToString()}");
}
catch (Exception ex)
{
MessageBox.Show($@"Error: {ex}");
}
}
... View more
09-13-2021
04:16 PM
|
0
|
1
|
3520
|
|
POST
|
You can use the following method of the Layer class to change the DisplayCache settings: SetDisplayCacheType (DisplayCacheType cacheType) ArcGIS Pro 2.8 API Reference Guide - Layer Class Members—ArcGIS Pro DisplayCacheType (ArcGIS Pro 2.8 API Reference Guide - DisplayCacheType Enumeration—ArcGIS Pro ) allows the following options: InSession Cache is maintained in session. MaxAge Cache expires when it reaches the set maximum age. None No cache. Permanent Cache is permanent.
... View more
09-13-2021
09:58 AM
|
0
|
2
|
2271
|
|
POST
|
I coded your sample snippet in the code below and also found a bug in my attached solution. First be bug: The ICommand property in the viewmodel was not named properly it should have been named: /// <summary>
/// hook existing ArcGIS Pro Tool Button
/// </summary>
public ICommand CmdCreatePolygon => FrameworkApplication.GetPlugInWrapper("CreatePolygon_PolygonTool") as ICommand; Second: here is my test snippet that shows the chained edit operation (on the same layer): /// <summary>
/// Fires once the user completes the on-screen sketch. In the context of a construction tool the sketch geometry
/// is used for the shape of the feature.
/// </summary>
/// <param name="geometry">The sketch geometry the user digitized on the screen.</param>
/// <returns></returns>
protected override Task<bool> OnSketchCompleteAsync(Geometry geometry)
{
// find and use the first polygon layer
var _polygonLayer = MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(lyr => lyr.ShapeType == esriGeometryType.esriGeometryPolygon).FirstOrDefault();
if (_polygonLayer == null)
{
MessageBox.Show("Cannot find a Polygon layer to save constructed geometries");
}
if (geometry == null || _polygonLayer == null)
return Task.FromResult(false);
var dockPane = Module1.ActiveDockPane;
if (dockPane == null)
{
MessageBox.Show($@"Unable to find the Add Polygon dockpane");
}
return QueuedTask.Run(() =>
{
// create an edit operation
var editOperation = new EditOperation
{
Name = $@"Create polygon in '{_polygonLayer.Name}'",
ProgressMessage = "Working...",
CancelMessage = "Operation canceled",
ErrorMessage = "Error creating point"
};
long newObjectID = -1;
// update attributes
Dictionary<string, object> attributes = new Dictionary<string, object>
{
{ "TheString", "First edit operation" }
};
// queue the edit operation using the sketch geometry as the shape of the feature
editOperation.Create(_polygonLayer, geometry, object_id => newObjectID = object_id);
dockPane.UpdateStatusText($@"Created a polygon in '{_polygonLayer.Name}'");
//execute the operation
if (!editOperation.Execute())
{
return Task.FromResult(false);
}
// chain to create a new operation
var opChain = editOperation.CreateChainedOperation();
attributes["TheString"] = "Chained operation";
opChain.Modify (_polygonLayer, newObjectID, attributes);
return opChain.ExecuteAsync();
});
} and here is the result, which shows that the chained edit did update the attribute table (by updating the "TheString" field with the "Chained operation" status:
... View more
09-09-2021
11:31 AM
|
0
|
0
|
3521
|
|
POST
|
I attached a sample that uses the graphics layer in order to add the labels to your map. The sample uses a selected polyline (feature) and creates labels at a distance of 200 meters along the selected line (depending on the linear unit of your projection)
... View more
09-08-2021
03:28 PM
|
0
|
0
|
1867
|
|
POST
|
The attached project performs your workflow using a dockpane (not a WPF window). The project works on 2.7 or newer.
... View more
09-07-2021
08:24 AM
|
0
|
1
|
3546
|
|
POST
|
The ArcGIS Pro API's MesssageBox.Show method returns the same result as the MessageBox method in .Net. According to the .Net documentation these are the results that are returned (depending on the type of MessageBox): Show returns a MessageBoxResult value that specifies which button on a message box a user clicked. MessageBoxButton specifies which buttons are displayed on a message box. The result value that is returned from a message box depends on what buttons the message box has and how a user closes a message box: On a message box that has an OK button, OK is returned if a user clicks the OK button, clicks the Close button in the title bar, or presses the ESC key. On a message box that has an OK button and a Cancel button, OK is returned if a user clicks the OK button. If a user clicks the Cancel button or the Close button in the title bar, Cancel is returned. On a message box that has a Yes button and a No button, the Close button in the title bar is disabled. Consequently, Yes is returned if a user clicks the Yes button, and No is returned if a user clicks the No button. On a message box that has a Yes button, a No button, and a Cancel button, Yes is returned if the Yes button is clicked and No is returned if the No button is clicked. If a user clicks the Cancel button or the Close button in the title bar, Cancel is returned. See: https://docs.microsoft.com/en-us/dotnet/api/system.windows.messageboxresult?view=net-5.0
... View more
08-31-2021
09:28 AM
|
0
|
0
|
3372
|
|
POST
|
Do you want to 'lock in' a single fixed scale on you map display so that the map view only allows a that scale and hence all user zoom in/out actions as disabled? As if you were configuring a single scale option and checking the 'only display this scale' option?
... View more
08-26-2021
08:46 AM
|
0
|
1
|
2296
|
|
POST
|
You can use the following code to run actions on the UI thread - it is optimized as it first determines the 'thread context' the caller is running on: /// <summary>
/// utility function to enable an action to run on the UI thread (if not already)
/// </summary>
/// <param name="action">the action to execute</param>
/// <returns></returns>
internal static void RunOnUiThread(Action action)
{
try
{
if (IsOnUiThread)
action();
else
Application.Current.Dispatcher.BeginInvoke(action);
}
catch (Exception ex)
{
MessageBox.Show($@"Error in OpenAndActivateMap: {ex.Message}");
}
}
/// <summary>
/// Determines whether the calling thread is the thread associated with this
/// System.Windows.Threading.Dispatcher, the UI thread.
///
/// If called from a View model test it always returns true.
/// </summary>
public static bool IsOnUiThread => ArcGIS.Desktop.Framework.FrameworkApplication.TestMode || System.Windows.Application.Current.Dispatcher.CheckAccess(); You can then 'invoke' your UI actions like this: // simple action
RunOnUiThread(() => { /* UI action functionality */ });
// action with await
RunOnUiThread(async () => { /* UI action functionality with await */ }); I think in most cases you should refrain from using Dispatcher.Invoke because it's a blocking call (meaning it doesn't return to you until it has been executed), instead you should use Dispatcher.BeginInvoke which is non blocking. Also there's another issue I noticed as the following code snippet with block the 'main CIM thread' for 4 seconds: await QueuedTask.Run(() => {
Task.Delay(4000).Wait();
// delayed action
}); So I would recommend to run a 'delay' from an asynchronous task using something like this (this is a Pro button's OnClick method): internal class TestDelay : Button
{
protected override void OnClick()
{
_ = DelayTaskAsync();
System.Diagnostics.Debug.WriteLine("OnClick done");
}
static async Task DelayTaskAsync()
{
Task delay = Task.Delay(5000);
await delay;
// do your delayed action here
System.Diagnostics.Debug.WriteLine("Delayed action");
}
} This will not block the UI nor the CIM thread.
... View more
08-25-2021
07:36 AM
|
2
|
1
|
4899
|
|
POST
|
To add the overlay control to the active map you can use this method: MapView.AddOverlayControl. This method takes a MapViewOverlayControl as its parameter. public void AddOverlayControl(IMapViewOverlayControl overlayControl) Let me know if this works, otherwise I will create a sample snippet.
... View more
08-24-2021
10:20 AM
|
1
|
2
|
4920
|
|
POST
|
When I played around with my sample to find the difference I was not able to get the 'mapped' field populated anymore. I passed the sample on to the GeoProcessing project engineer, i really can't see anything wrong with the code. Also, I think the GP tool should report a warning or error if the 'mapped' field cannot be populated. I will let you know if we find the cause of this issue. Thanks for you patience and reporting this issue.
... View more
08-20-2021
12:10 PM
|
0
|
0
|
2631
|
|
POST
|
I used the following snippet with different schemas (I attached my sample ArcGIS Pro project file): protected override async void OnClick()
{
try
{
var environments = Geoprocessing.MakeEnvironmentArray(overwriteoutput: true);
var toolPath = "management.Append";
var shapefilePath = @"C:\Users\wolf2125\Documents\ArcGIS\Projects\GPTestAppendTool\ShapeFile\base_state.shp";
var shapefileName = System.IO.Path.GetFileNameWithoutExtension(shapefilePath);
var destintationTable = "BaseState";
var fields = new string[] {
$@"AREA ""AREA"" true false false 8 Double 0 0,First,#,{shapefilePath},AREA,-1,-1",
$@"PERIMETER ""PERIMETER"" true false false 8 Double 0 0,First,#,{shapefilePath},PERIMETER,-1,-1",
$@"BASEMAP_ ""BASEMAP_"" true false false 8 Double 0 0,First,#,{shapefilePath},BASEMAP_,-1,-1",
$@"BASEMAP_ID ""BASEMAP_ID"" true false false 8 Double 0 0,First,#,{shapefilePath},BASEMAP_ID,-1,-1",
$@"Test ""TYPE"" true false false 10 Text 0 0,First,#,{shapefileName},TYPE,0,10" };
var fieldMap = string.Join(";", fields);
var Parameters = Geoprocessing.MakeValueArray(shapefileName, destintationTable, "NO_TEST", fieldMap, "", "");
var gpResult = await Geoprocessing.ExecuteToolAsync(toolPath, Parameters, environments);
Geoprocessing.ShowMessageBox(gpResult.Messages, "GP Messages", gpResult.IsFailed ? GPMessageBoxStyle.Error : GPMessageBoxStyle.Default);
}
catch (Exception ex)
{
MessageBox.Show($@"Error: {ex}");
}
} Actually I just noticed that I had changed the alias name for the "Test" column in the destination table from "Type" to "Test" but I forgot to make the change in the code snippet. However, i seems that the alias name doesn't matter for this tool.
... View more
08-19-2021
03:21 PM
|
0
|
0
|
2637
|
|
POST
|
You should be able to compile/build and run your project despite these error messages. The messages are coming from the 'XAML Designer' and if you close all XAML windows and rebuild you shouldn't get any messages. The problem's cause was this: In 2.8 all Pro assemblies were switched from mixed (x86 and x64) to 64 bit only. Unfortunately the XAML designer loader cannot load x64 bit assemblies. Here is a knowledge base article to that regard: Error: Could not load file or assembly 'ArcGIS.Desktop.Framework, Culture=neutral' or one of its dependencies. The system cannot find the file specified. (esri.com) Hopefully the upcoming release of Visual Studio 2022 will fix this issue.
... View more
08-19-2021
09:22 AM
|
1
|
1
|
11655
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 10-29-2025 10:48 AM | |
| 1 | 05-24-2021 09:04 AM | |
| 1 | 12-03-2020 08:44 AM | |
| 1 | 10-07-2025 07:27 AM | |
| 2 | 12-29-2025 10:03 AM |
| Online Status |
Offline
|
| Date Last Visited |
05-21-2026
01:59 PM
|