// Some times when using EditOperation.Modify you can unknowingly be attempting to set// an attribute to value // setting // In this scenario the Modify action will detect that nothing is required// and do nothing. Because no actions have occurred, the// Consequently the Execute operation will fail. if(!opEdit.IsEmpty)
opEdit.Execute();
Edit Operation Create Features
varcreateFeatures=new EditOperation(){Name="Create Features"};//Create a feature with a polygonvartoken= createFeatures.Create(featureLayer, polygon);if(createFeatures.IsSucceeded){// token.ObjectID wll be populated with the objectID of the created feature after Execute has been successful}//Do a create features and set attributesvarattributes=newDictionary<string,object>();
attributes.Add("SHAPE", polygon);
attributes.Add("NAME","Corner Market");
attributes.Add("SIZE",1200.5);
attributes.Add("DESCRIPTION","Corner Market");
createFeatures.Create(featureLayer, attributes);//Create features using the current template//Must be within a MapTool
createFeatures.Create(this.CurrentTemplate, polygon);//Execute to execute the operation//Must be called within QueuedTask.Runif(!createFeatures.IsEmpty){
createFeatures.Execute();//Execute will return true if the operation was successful and false if not.}//or use async flavor//await createFeatures.ExecuteAsync();
Create a feature using the current template
varmyTemplate= ArcGIS.Desktop.Editing.Templates.EditingTemplate.Current;//Create edit operation and executevarop=new ArcGIS.Desktop.Editing.EditOperation(){Name="Create my feature"};
op.Create(myTemplate, geometry);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Create feature from a modified inspector
varinsp=new ArcGIS.Desktop.Editing.Attributes.Inspector();
insp.Load(layer,86);
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{// modify attributes if necessary// insp["Field1"] = newValue;//Create new feature from an existing inspector (copying the feature)varcreateOp=new EditOperation(){Name="Create from insp"}; createOp.Create(insp.MapMember, insp.ToDictionary(a => a.FieldName,a => a.CurrentValue));if(!createOp.IsEmpty){varresult= createOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Create features from a CSV file
//Run on MCT
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//Create the edit operationvarcreateOperation=new ArcGIS.Desktop.Editing.EditOperation(){Name="Generate points",SelectNewFeatures=false};// determine the shape field name - it may not be 'Shape' stringshapeField= layer.GetFeatureClass().GetDefinition().GetShapeField();//Loop through csv dataforeach(var item in csvData){//Create the point geometry ArcGIS.Core.Geometry.MapPoint newMapPoint= ArcGIS.Core.Geometry.MapPointBuilderEx.CreateMapPoint(item.X, item.Y);// include the attributes via a dictionaryvaratts=newDictionary<string,object>(); atts.Add("StopOrder", item.StopOrder); atts.Add("FacilityID", item.FacilityID); atts.Add(shapeField, newMapPoint);// queue feature creation createOperation.Create(layer, atts);}// execute the edit (feature creation) operationif(createOperation.IsEmpty){return createOperation.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}elsereturnfalse;});
Edit Operation Create row in a table using a table template
vartableTemplate= standaloneTable.GetTemplates().FirstOrDefault();varcreateRow=new EditOperation(){Name="Create a row in a table"};//Creating a new row in a standalone table using the table template of your choice
createRow.Create(tableTemplate);if(!createRow.IsEmpty){varresult= createRow.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Edit Operation Clip Features
varclipFeatures=new EditOperation(){Name="Clip Features"};
clipFeatures.Clip(featureLayer, oid, clipPoly, ClipMode.PreserveArea);//Execute to execute the operation//Must be called within QueuedTask.Runif(!clipFeatures.IsEmpty){varresult= clipFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await clipFeatures.ExecuteAsync();
Edit Operation Cut Features
varselect= MapView.Active.SelectFeatures(clipPoly);varcutFeatures=new EditOperation(){Name="Cut Features"};
cutFeatures.Split(featureLayer, oid, cutLine);//Cut all the selected features in the active view//Select using a polygon (for example)//at 2.x - var kvps = MapView.Active.SelectFeatures(polygon).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//cutFeatures.Split(kvps, cutLine);varsset= MapView.Active.SelectFeatures(polygon);
cutFeatures.Split(sset, cutLine);//Execute to execute the operation//Must be called within QueuedTask.Runif(!cutFeatures.IsEmpty){varresult= cutFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await cutFeatures.ExecuteAsync();
Edit Operation Delete Features
vardeleteFeatures=new EditOperation(){Name="Delete Features"};vartable= MapView.Active.Map.StandaloneTables[0];//Delete a row in a standalone table
deleteFeatures.Delete(table, oid);//Delete all the selected features in the active view//Select using a polygon (for example)//at 2.x - var selection = MapView.Active.SelectFeatures(polygon).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//deleteFeatures.Delete(selection);varselection= MapView.Active.SelectFeatures(polygon);
deleteFeatures.Delete(selection);//Execute to execute the operation//Must be called within QueuedTask.Runif(!deleteFeatures.IsEmpty){varresult= deleteFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await deleteFeatures.ExecuteAsync();
Edit Operation Duplicate Features
{varduplicateFeatures=new EditOperation(){Name="Duplicate Features"};//Duplicate with an X and Y offset of 500 map units//At 2.x duplicateFeatures.Duplicate(featureLayer, oid, 500.0, 500.0, 0.0);//Execute to execute the operation//Must be called within QueuedTask.Runvarinsp2=new Inspector();
insp2.Load(featureLayer, oid);vargeom= insp2["SHAPE"]as Geometry;varrtoken= duplicateFeatures.Create(insp2.MapMember, insp2.ToDictionary(a => a.FieldName,a => a.CurrentValue));if(!duplicateFeatures.IsEmpty){if(duplicateFeatures.Execute())//Execute and ExecuteAsync will return true if the operation was successful and false if not{varmodifyOp= duplicateFeatures.CreateChainedOperation();
modifyOp.Modify(featureLayer,(long)rtoken.ObjectID, GeometryEngine.Instance.Move(geom,500.0,500.0));if(!modifyOp.IsEmpty){varresult= modifyOp.Execute();}}}}
Edit Operation Explode Features
varexplodeFeatures=new EditOperation(){Name="Explode Features"};//Take a multipart and convert it into one feature per part//Provide a list of ids to convert multiple
explodeFeatures.Explode(featureLayer,newList<long>(){ oid },true);//Execute to execute the operation//Must be called within QueuedTask.Runif(!explodeFeatures.IsEmpty){varresult= explodeFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await explodeFeatures.ExecuteAsync();
Edit Operation Merge Features
varmergeFeatures=new EditOperation(){Name="Merge Features"};//Merge three features into a new feature using defaults//defined in the current template//At 2.x -//mergeFeatures.Merge(this.CurrentTemplate as EditingFeatureTemplate, featureLayer, new List<long>() { 10, 96, 12 });
mergeFeatures.Merge(this.CurrentTemplate as EditingRowTemplate, featureLayer,newList<long>(){10,96,12});//Merge three features into a new feature in the destination layer
mergeFeatures.Merge(destinationLayer, featureLayer,newList<long>(){10,96,12});//Use an inspector to set the new attributes of the merged featurevarinspector=new Inspector();
inspector.Load(featureLayer, oid);//base attributes on an existing feature//change attributes for the new feature
inspector["NAME"]="New name";
inspector["DESCRIPTION"]="New description";//Merge features into a new feature in the same layer using the//defaults set in the inspector
mergeFeatures.Merge(featureLayer,newList<long>(){10,96,12}, inspector);//Execute to execute the operation//Must be called within QueuedTask.Runif(!mergeFeatures.IsEmpty){varresult= mergeFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await mergeFeatures.ExecuteAsync();
Edit Operation Modify single feature
varmodifyFeature=new EditOperation(){Name="Modify a feature"};//use an inspectorvarmodifyInspector=new Inspector();
modifyInspector.Load(featureLayer, oid);//base attributes on an existing feature//change attributes for the new feature
modifyInspector["SHAPE"]=polygon;//Update the geometry
modifyInspector["NAME"]="Updated name";//Update attribute(s)
modifyFeature.Modify(modifyInspector);//update geometry and attributes using overloadvarfeatureAttributes=newDictionary<string,object>();
featureAttributes["NAME"]="Updated name";//Update attribute(s)
modifyFeature.Modify(featureLayer, oid, polygon, featureAttributes);//Execute to execute the operation//Must be called within QueuedTask.Runif(!modifyFeature.IsEmpty){varresult= modifyFeature.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await modifyFeatures.ExecuteAsync();
Edit Operation Modify multiple features
//Search by attributevarqueryFilter=new QueryFilter(){WhereClause="OBJECTID < 1000000"};//Create list of oids to updatevaroidSet=newList<long>();using(varrc= featureLayer.Search(queryFilter)){while(rc.MoveNext()){using(varrecord= rc.Current){
oidSet.Add(record.GetObjectID());}}}//create and execute the edit operationvarmodifyFeatures=new EditOperation(){Name="Modify features"};
modifyFeatures.ShowProgressor =true;varmultipleFeaturesInsp=new Inspector();
multipleFeaturesInsp.Load(featureLayer, oidSet);
multipleFeaturesInsp["MOMC"]=24;
modifyFeatures.Modify(multipleFeaturesInsp);if(!modifyFeatures.IsEmpty){varresult= modifyFeatures.ExecuteAsync();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Search for layer features and update a field
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//find layervardisLayer= ArcGIS.Desktop.Mapping.MapView.Active.Map.FindLayers("Distribution mains").FirstOrDefault()as BasicFeatureLayer;//Search by attributevarfilter=new ArcGIS.Core.Data.QueryFilter {WhereClause="CONTRACTOR = 'KCGM'"};varoids=newList<long>();using(varrc= disLayer.Search(filter)){//Create list of oids to updatewhile(rc.MoveNext()){using(varrecord= rc.Current){ oidSet.Add(record.GetObjectID());}}}//Create edit operation varmodifyOp=new EditOperation(){Name="Update date"};// load features into inspector and update fieldvardateInsp=new ArcGIS.Desktop.Editing.Attributes.Inspector(); dateInsp.Load(disLayer, oids); dateInsp["InspDate"]="9/21/2013";// modify and execute modifyOp.Modify(insp);if(!modifyOp.IsEmpty){varresult= modifyOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Move features
//Get all of the selected ObjectIDs from the layer.varfirstLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault();varselectionfromMap= firstLayer.GetSelection();// set up a dictionary to store the layer and the object IDs of the selected featuresvarselectionDictionary=newDictionary<MapMember,List<long>>();
selectionDictionary.Add(firstLayer as MapMember, selectionfromMap.GetObjectIDs().ToList());varmoveFeature=new EditOperation(){Name="Move features"};//at 2.x - moveFeature.Move(selectionDictionary, 10, 10); //specify your units along axis to move the geometry
moveFeature.Move(SelectionSet.FromDictionary(selectionDictionary),10,10);//specify your units along axis to move the geometryif(!moveFeature.IsEmpty){varresult= moveFeature.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Move feature to a specific coordinate
//Get all of the selected ObjectIDs from the layer.varabLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault();varmySelection= abLayer.GetSelection();varselOid= mySelection.GetObjectIDs().FirstOrDefault();varmoveToPoint=new MapPointBuilderEx(1.0,2.0,3.0,4.0, MapView.Active.Map.SpatialReference);//can pass in coordinates.varmodifyFeatureCoord=new EditOperation(){Name="Move features"};
modifyFeatureCoord.Modify(abLayer, selOid, moveToPoint.ToGeometry());//Modify the feature to the new geometry if(!modifyFeatureCoord.IsEmpty){varresult= modifyFeatureCoord.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Edit Operation Planarize Features
// note - EditOperation.Planarize requires a standard license. // An exception will be thrown if Pro is running under a basic license. varplanarizeFeatures=new EditOperation(){Name="Planarize Features"};//Planarize one or more features
planarizeFeatures.Planarize(featureLayer,newList<long>(){ oid });//Execute to execute the operation//Must be called within QueuedTask.Runif(!planarizeFeatures.IsEmpty){varresult= planarizeFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await planarizeFeatures.ExecuteAsync();
Edit Operation ParallelOffset
//Create parallel features from the selected features//find the roads layervarroadsLayer= MapView.Active.Map.FindLayers("Roads").FirstOrDefault();//instantiate parallelOffset builder and set parametersvarparOffsetBuilder=new ParallelOffset.Builder(){Selection= MapView.Active.Map.GetSelection(),Template= roadsLayer.GetTemplate("Freeway"),Distance=200,Side= ParallelOffset.SideType.Both,Corner= ParallelOffset.CornerType.Mitered,Iterations=1,AlignConnected=false,CopyToSeparateFeatures=false,RemoveSelfIntersectingLoops=true};//create editoperation and executevarparallelOp=new EditOperation();
parallelOp.Create(parOffsetBuilder);if(!parallelOp.IsEmpty){varresult= parallelOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Edit Operation Reshape Features
varreshapeFeatures=new EditOperation(){Name="Reshape Features"};
reshapeFeatures.Reshape(featureLayer, oid, modifyLine);//Reshape a set of features that intersect some geometry....//at 2.x - var selFeatures = MapView.Active.GetFeatures(modifyLine).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//reshapeFeatures.Reshape(selFeatures, modifyLine);
reshapeFeatures.Reshape(MapView.Active.GetFeatures(modifyLine), modifyLine);//Execute to execute the operation//Must be called within QueuedTask.Runif(!reshapeFeatures.IsEmpty){varresult= reshapeFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await reshapeFeatures.ExecuteAsync();
Edit Operation Rotate Features
varrotateFeatures=new EditOperation(){Name="Rotate Features"};//Rotate works on a selected set of features//Get all features that intersect a polygon//at 2.x - var rotateSelection = MapView.Active.GetFeatures(polygon).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//rotateFeatures.Rotate(rotateSelection, origin, Math.PI / 2);//Rotate selected features 90 deg about "origin"
rotateFeatures.Rotate(MapView.Active.GetFeatures(polygon), origin, Math.PI /2);//Execute to execute the operation//Must be called within QueuedTask.Runif(!rotateFeatures.IsEmpty){varresult= rotateFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await rotateFeatures.ExecuteAsync();
Edit Operation Scale Features
varscaleFeatures=new EditOperation(){Name="Scale Features"};//Rotate works on a selected set of features//var scaleSelection = MapView.Active.GetFeatures(polygon).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//scaleFeatures.Scale(scaleSelection, origin, 2.0, 2.0, 0.0);//Scale the selected features by 2.0 in the X and Y direction
scaleFeatures.Scale(MapView.Active.GetFeatures(polygon), origin,2.0,2.0,0.0);//Execute to execute the operation//Must be called within QueuedTask.Runif(!scaleFeatures.IsEmpty){varresult= scaleFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await scaleFeatures.ExecuteAsync();
Edit Operation Split Features
varsplitFeatures=new EditOperation(){Name="Split Features"};varsplitPoints=newList<MapPoint>(){ mp1, mp2, mp3 };//Split the feature at 3 points
splitFeatures.Split(featureLayer, oid, splitPoints);// split using percentagevarsplitByPercentage=new SplitByPercentage(){Percentage=33,SplitFromStartPoint=true};
splitFeatures.Split(featureLayer, oid, splitByPercentage);// split using equal partsvarsplitByEqualParts=new SplitByEqualParts(){NumParts=3};
splitFeatures.Split(featureLayer, oid, splitByEqualParts);// split using single distancevarsplitByDistance=new SplitByDistance(){Distance=27.3,SplitFromStartPoint=false};
splitFeatures.Split(featureLayer, oid, splitByDistance);// split using varying distancevardistances=newList<double>(){12.5,38.2,89.99};varsplitByVaryingDistance=new SplitByVaryingDistance(){Distances=distances,SplitFromStartPoint=true,ProportionRemainder=true};
splitFeatures.Split(featureLayer, oid, splitByVaryingDistance);//Execute to execute the operation//Must be called within QueuedTask.Runif(!splitFeatures.IsEmpty){varresult= splitFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await splitAtPointsFeatures.ExecuteAsync();
Edit Operation Transform Features
vartransformFeatures=new EditOperation(){Name="Transform Features"};//Transform a selected set of features//At 2.x - var transformSelection = MapView.Active.GetFeatures(polygon).Select(// k => new KeyValuePair<MapMember, List<long>>(k.Key as MapMember, k.Value));//transformFeatures.Transform(transformSelection, linkLayer);////Transform just a layer//transformFeatures.Transform(featureLayer, linkLayer);////Perform an affine transformation//transformFeatures.TransformAffine(featureLayer, linkLayer);varaffine_transform=new TransformByLinkLayer(){LinkLayer=linkLayer,TransformType= TransformMethodType.Affine //TransformMethodType.Similarity};//Transform a selected set of features
transformFeatures.Transform(MapView.Active.GetFeatures(polygon), affine_transform);//Perform an affine transformation
transformFeatures.Transform(featureLayer, affine_transform);//Execute to execute the operation//Must be called within QueuedTask.Runif(!transformFeatures.IsEmpty){varresult= transformFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await transformFeatures.ExecuteAsync();
Edit Operation Rubbersheet Features
//Perform rubbersheet by geometriesvarrubbersheetMethod=new RubbersheetByGeometries(){RubbersheetType= RubbersheetMethodType.Linear,//The RubbersheetType can be Linear of NearestNeighborLinkLines=linkLines,//IEnumerable list of link lines (polylines)AnchorPoints=anchorPoints,//IEnumerable list of anchor points (map points)LimitedAdjustmentAreas=limitedAdjustmentAreas//IEnumerable list of limited adjustment areas (polygons)};varrubbersheetOp=new EditOperation();//Performs linear rubbersheet transformation on the features belonging to "layer" that fall within the limited adjustment areas
rubbersheetOp.Rubbersheet(layer, rubbersheetMethod);//Execute the operationif(!rubbersheetOp.IsEmpty){varresult= rubbersheetOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//Alternatively, you can also perform rubbersheet by layervarrubbersheetMethod2=new RubbersheetByLayers(){RubbersheetType= RubbersheetMethodType.NearestNeighbor,//The RubbersheetType can be Linear of NearestNeighborLinkLayer=linkLayer,AnchorPointLayer=anchorPointsLayer,LimitedAdjustmentAreaLayer=limitedAdjustmentAreaLayer};//Performs nearest neighbor rubbersheet transformation on the features belonging to "layer" that fall within the limited adjustment areas
rubbersheetOp.Rubbersheet(layer, rubbersheetMethod2);if(!rubbersheetOp.IsEmpty){//Execute the operationvarresult= rubbersheetOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Edit Operation Perform a Clip, Cut, and Planarize
//Multiple operations can be performed by a single//edit operation.varclipCutPlanarizeFeatures=new EditOperation(){Name="Clip, Cut, and Planarize Features"};
clipCutPlanarizeFeatures.Clip(featureLayer, oid, clipPoly);
clipCutPlanarizeFeatures.Split(featureLayer, oid, cutLine);
clipCutPlanarizeFeatures.Planarize(featureLayer, oid);if(!clipCutPlanarizeFeatures.IsEmpty){//Note: An edit operation is a single transaction. //Execute the operations (in the order they were declared)
clipCutPlanarizeFeatures.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}//or use async flavor//await clipCutPlanarizeFeatures.ExecuteAsync();
Edit Operation Chain Edit Operations
//Chaining operations is a special case. Use "Chained Operations" when you require multiple transactions //to be undo-able with a single "Undo".//The most common use case for operation chaining is creating a feature with an attachment. //Adding an attachment requires the object id (of a new feature) has already been created. vareditOperation1=new EditOperation(){Name=string.Format("Create point in '{0}'", CurrentTemplate.Layer.Name)};longnewFeatureID=-1;//The Create operation has to execute so we can get an object_idvartoken2= editOperation1.Create(this.CurrentTemplate, polygon);//Must be within a QueuedTask
editOperation1.Execute();//Note: Execute and ExecuteAsync will return true if the operation was successful and false if notif(editOperation1.IsSucceeded){newFeatureID=(long)token2.ObjectID;//Now, because we have the object id, we can add the attachment. As we are chaining it, adding the attachment //can be undone as part of the "Undo Create" operation. In other words, only one undo operation will show on the //Pro UI and not two.vareditOperation2= editOperation1.CreateChainedOperation();//Add the attachment using the new feature id
editOperation2.AddAttachment(this.CurrentTemplate.Layer, newFeatureID,@"C:\data\images\Hydrant.jpg");//Execute the chained edit operation. editOperation1 and editOperation2 show up as a single Undo operation//on the UI even though we had two transactions
editOperation2.Execute();}
Edit Operation add attachment via RowToken
//ArcGIS Pro 2.5 extends the EditOperation.AddAttachment method to take a RowToken as a parameter.//This allows you to create a feature, using EditOperation.CreateEx, and add an attachment in one transaction.vareditOpAttach=new EditOperation();
editOperation1.Name =string.Format("Create point in '{0}'", CurrentTemplate.Layer.Name);varattachRowToken= editOpAttach.Create(this.CurrentTemplate, polygon);
editOpAttach.AddAttachment(attachRowToken,@"c:\temp\image.jpg");//Must be within a QueuedTaskif(!editOpAttach.IsEmpty){varresult= editOpAttach.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Order edits sequentially
// perform an edit and then a split as one operation.
QueuedTask.Run(()=>{varqueryFilter=new QueryFilter(){WhereClause="OBJECTID = "+ oid.ToString()};// create an edit operation and name.varop=new EditOperation(){Name="modify followed by split"};// set the ExecuteMode op.ExecuteMode = ExecuteModeType.Sequential;using(varrowCursor= fc.Search(queryFilter,false)){while(rowCursor.MoveNext()){using(varfeature= rowCursor.Current as Feature){ op.Modify(feature,"NAME", newName);}}} op.Split(layer, oid, splitLine);if(!op.IsEmpty){boolresult= op.Execute();}// else// The operation doesn't make any changes to the database so if executed it will fail});
SetOnUndone, SetOnRedone, SetOnComitted
// SetOnUndone, SetOnRedone and SetOnComitted can be used to manage // external actions(such as writing to a log table) that are associated with // each edit operation.//get selected feature and update attributevarselectedFeatures= MapView.Active.Map.GetSelection();vartestInspector=new Inspector();
testInspector.Load(selectedFeatures.ToDictionary().Keys.First(), selectedFeatures.ToDictionary().Values.First());
testInspector["Name"]="test";//create and execute the edit operationvarupdateTestField=new EditOperation(){Name="Update test field"};
updateTestField.Modify(insp);//actions for SetOn...
updateTestField.SetOnUndone(()=>{//Sets an action that will be called when this operation is undone. Debug.WriteLine("Operation is undone");});
updateTestField.SetOnRedone(()=>{//Sets an action that will be called when this editoperation is redone. Debug.WriteLine("Operation is redone");});
updateTestField.SetOnComitted((boolb)=>//called on edit session save(true)/discard(false).{// Sets an action that will be called when this editoperation is committed. Debug.WriteLine("Operation is committed");});if(!updateTestField.IsEmpty){varresult= updateTestField.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}
Convert vertices in a polyline to a Control Point
//Control points are special vertices used to apply symbol effects to line or polygon features.//By default, they appear as diamonds when you edit them.//They can also be used to migrate representations from ArcMap to features in ArcGIS Pro.
QueuedTask.Run(()=>{//iterate through the points in the polyline.varlineLayerCursor= lineLayer.GetSelection().Search();varlineVertices=newList<MapPoint>();longoid=-1;while(lineLayerCursor.MoveNext()){varlineFeature= lineLayerCursor.Current as Feature;varline= lineFeature.GetShape()as Polyline;intvertexIndex=1;oid= lineFeature.GetObjectID();//Each point is converted into a MapPoint and gets added to a list. foreach(var point in line.Points){MapPointBuilderExmapPointBuilderEx=new MapPointBuilderEx(point);//Changing the vertex 6 and 7 to control pointsif(vertexIndex==6||vertexIndex==7){//These points are made "ID Aware" and the IDs are set to be 1. mapPointBuilderEx.HasID =true; mapPointBuilderEx.ID =1;} lineVertices.Add(mapPointBuilderEx.ToGeometry()as MapPoint);vertexIndex++;}}//create a new polyline using the point collection.varnewLine= PolylineBuilderEx.CreatePolyline(lineVertices);//edit operation to modify the original line with the new line that contains control points. changeVertexIDOperation.Modify(lineLayer, oid, newLine); changeVertexIDOperation.Execute();});
Enable Editing
Enable Editing
// if not editingif(!Project.Current.IsEditingEnabled){varres= MessageBox.Show("You must enable editing to use editing tools. Would you like to enable editing?","Enable Editing?", System.Windows.MessageBoxButton.YesNoCancel);if(res== System.Windows.MessageBoxResult.No ||res== System.Windows.MessageBoxResult.Cancel){return;}
Project.Current.SetIsEditingEnabledAsync(true);}
Disable Editing
// if editingif(Project.Current.IsEditingEnabled){varres= MessageBox.Show("Do you want to disable editing? Editing tools will be disabled","Disable Editing?", System.Windows.MessageBoxButton.YesNoCancel);if(res== System.Windows.MessageBoxResult.No ||res== System.Windows.MessageBoxResult.Cancel){return;}//we must check for editsif(Project.Current.HasEdits){res= MessageBox.Show("Save edits?","Save Edits?", System.Windows.MessageBoxButton.YesNoCancel);if(res== System.Windows.MessageBoxResult.Cancel)return;elseif(res== System.Windows.MessageBoxResult.No)
Project.Current.DiscardEditsAsync();else{
Project.Current.SaveEditsAsync();}}
Project.Current.SetIsEditingEnabledAsync(false);}
Row Events
Subscribe to Row Events
protectedvoidSubscribeRowEvent(){
QueuedTask.Run(()=>{//Listen for row events on a layervarfeatLayer= MapView.Active.GetSelectedLayers().First()as FeatureLayer;varlayerTable= featLayer.GetTable();//subscribe to row eventsvarrowCreateToken= RowCreatedEvent.Subscribe(OnRowCreated, layerTable);varrowChangeToken= RowChangedEvent.Subscribe(OnRowChanged, layerTable);varrowDeleteToken= RowDeletedEvent.Subscribe(OnRowDeleted, layerTable);});}protectedvoidOnRowCreated(RowChangedEventArgsargs){}protectedvoidOnRowChanged(RowChangedEventArgsargs){}protectedvoidOnRowDeleted(RowChangedEventArgsargs){}
Create a record in a separate table in the Map within Row Events
// Use the EditOperation in the RowChangedEventArgs to append actions to be executed. // Your actions will become part of the operation and combined into one item on the undo stackprivatevoidHookRowCreatedEvent(){// subscribe to the RowCreatedEventTabletable= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();
RowCreatedEvent.Subscribe(MyRowCreatedEvent, table);}privatevoidMyRowCreatedEvent(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.// get the edit operationvarparentEditOp= args.Operation;// set up some attributesvarattribs=newDictionary<string,object>{};
attribs.Add("Layer","Parcels");
attribs.Add("Description","OID: "+ args.Row.GetObjectID().ToString()+""+ DateTime.Now.ToShortTimeString());//create a record in an audit tablevarsTable= MapView.Active.Map.FindStandaloneTables("EditHistory").First();vartable= sTable.GetTable();
parentEditOp.Create(table, attribs);}
Create a record in a separate table within Row Events
// Use the EditOperation in the RowChangedEventArgs to append actions to be executed. // Your actions will become part of the operation and combined into one item on the undo stackprivatevoidHookCreatedEvent(){// subscribe to the RowCreatedEventTabletable= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();
RowCreatedEvent.Subscribe(OnRowCreatedEvent, table);}privatevoidOnRowCreatedEvent(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.// update a separate table not in the map when a row is created// You MUST use the ArcGIS.Core.Data API to edit the table. Do NOT// use a new edit operation in the RowEvent callbackstry{// get the edit operationvarparentEditOp= args.Operation;// set up some attributesvarattribs=newDictionary<string,object>{};
attribs.Add("Description","OID: "+ args.Row.GetObjectID().ToString()+""+ DateTime.Now.ToShortTimeString());// update Notes table with information about the new featureusing(vargeoDatabase=new Geodatabase(new FileGeodatabaseConnectionPath(new Uri(Project.Current.DefaultGeodatabasePath)))){using(vartable= geoDatabase.OpenDataset<Table>("Notes")){
parentEditOp.Create(table, attribs);}}}catch(Exceptione){
MessageBox.Show($@"Error in OnRowCreated for OID: {args.Row.GetObjectID()} : {e.ToString()}");}}
Modify a record within Row Events - using Row.Store
privatevoidHookRowChangedEvent(){// subscribe to the RowChangedEventTabletable= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();
RowChangedEvent.Subscribe(OnRowChangedEvent, table);}privateGuid_currentRowChangedGuid= Guid.Empty;protectedvoidOnRowChangedEvent(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.varrow= args.Row;// check for re-entry (only if row.Store is called)if(_currentRowChangedGuid== args.Guid)return;varfldIdx= row.FindField("POLICE_DISTRICT");if(fldIdx!=-1){//Validate any change to �police district�// cancel the edit if validation on the field failsif(row.HasValueChanged(fldIdx)){// cancel edit with invalid district (5)varvalue= row["POLICE_DISTRICT"].ToString();if(value=="5"){//Cancel edits with invalid �police district� values
args.CancelEdit($"Police district {row["POLICE_DISTRICT"]} is invalid");}}// update the description field
row["Description"]="Row Changed";// this update with cause another OnRowChanged event to occur// keep track of the row guid to avoid recursion_currentRowChangedGuid= args.Guid;
row.Store();_currentRowChangedGuid= Guid.Empty;}}
Modify a record within Row Events - using EditOperation.Modify
privatevoidHookChangedEvent(){// subscribe to the RowChangedEventTabletable= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();
RowChangedEvent.Subscribe(MyRowChangedEvent, table);}privatevoidMyRowChangedEvent(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.//example of modifying a field on a row that has been createdvarparentEditOp= args.Operation;// avoid recursionif(_lastEdit!= args.Guid){//update field on change
parentEditOp.Modify(args.Row,"ZONING","New");_lastEdit= args.Guid;}}
Determine if Geometry Changed while editing
privatestaticFeatureLayerfeatureLayer;privatestaticvoidDetermineGeometryChange(){featureLayer= MapView.Active?.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault();if(featureLayer==null)return;
QueuedTask.Run(()=>{//Listen to the RowChangedEvent that occurs when a Row is changed. ArcGIS.Desktop.Editing.Events.RowChangedEvent.Subscribe(OnRowChangedEvent2, featureLayer.GetTable());});}privatestaticvoidOnRowChangedEvent2(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.//Get the layer's definitionvarlyrDefn= featureLayer.GetFeatureClass().GetDefinition();//Get the shape field of the feature classstringshapeField= lyrDefn.GetShapeField();//Index of the shape fieldvarshapeIndex= lyrDefn.FindField(shapeField);//Original geometry of the modified rowvargeomOrig= args.Row.GetOriginalValue(shapeIndex)as Geometry;//New geometry of the modified rowvargeomNew= args.Row[shapeIndex]as Geometry;//Compare the twoboolshapeChanged= geomOrig.IsEqual(geomNew);}
Cancel a delete
publicvoidStopADelete(){// subscribe to the RowDeletedEvent for the appropriate tableTabletable= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();
RowDeletedEvent.Subscribe(OnRowDeletedEvent, table);}privateGuid_currentRowDeletedGuid= Guid.Empty;privatevoidOnRowDeletedEvent(RowChangedEventArgsargs){// RowEvent callbacks are always called on the QueuedTask so there is no need // to wrap your code within a QueuedTask.Run lambda.varrow= args.Row;// check for re-entry if(_currentRowDeletedGuid== args.Guid)return;// cancel the delete if the feature is in Police District 5varfldIdx= row.FindField("POLICE_DISTRICT");if(fldIdx!=-1){varvalue= row[fldIdx].ToString();if(value=="5"){//cancel with dialog// Note - feature edits on Hosted and Standard Feature Services cannot be cancelled.
args.CancelEdit("Delete Event\nAre you sure",true);// or cancel without a dialog// args.CancelEdit();}}_currentRowDeletedGuid= args.Guid;}
EditCompletedEvent
Subscribe to EditCompletedEvent
protectedvoidSubEditEvents(){//subscribe to editcompletedvareceToken= EditCompletedEvent.Subscribe(OnEce);}protected Task OnEce(EditCompletedEventArgsargs){//show number of edits
Console.WriteLine("Creates: "+ args.Creates.ToDictionary().Values.Sum(list => list.Count).ToString());
Console.WriteLine("Modifies: "+ args.Modifies.ToDictionary().Values.Sum(list => list.Count).ToString());
Console.WriteLine("Deletes: "+ args.Deletes.ToDictionary().Values.Sum(list => list.Count).ToString());return Task.FromResult(0);}
Inspector
Load a feature from a layer into the inspector
// get the first feature layer in the mapvarfirstFeatureLayer= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetLayersAsFlattenedList().OfType<ArcGIS.Desktop.Mapping.FeatureLayer>().FirstOrDefault();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the feature with ObjectID 'oid' into the inspectorawait inspector.LoadAsync(firstFeatureLayer, oid);
Load map selection into Inspector
// get the currently selected features in the mapvarselectedFeatures= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetSelection();// get the first layer and its corresponding selected feature OIDsvarfirstSelectionSet= selectedFeatures.ToDictionary().First();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawait inspector.LoadAsync(firstSelectionSet.Key, firstSelectionSet.Value);
Get selected feature's attribute value
QueuedTask.Run(()=>{// get the currently selected features in the mapvarselectedFeatures= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetSelection();// get the first layer and its corresponding selected feature OIDsvarfirstSelectionSet= selectedFeatures.ToDictionary().First();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDs inspector.Load(firstSelectionSet.Key, firstSelectionSet.Value);//get the value ofvarpscode= inspector["STATE_NAME"];varmyGeometry= inspector.Shape;});
Load map selection into Inspector and Change Attributes
// get the currently selected features in the mapvarselectedFeatures= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetSelection();// get the first layer and its corresponding selected feature OIDsvarfirstSelectionSet= selectedFeatures.ToDictionary().First();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawait inspector.LoadAsync(firstSelectionSet.Key, firstSelectionSet.Value);// assign the new attribute value to the field "Description"// if more than one features are loaded, the change applies to all features
inspector["Description"]="The new value.";// apply the changes as an edit operation await inspector.ApplyAsync();
Get a layers schema using Inspector
QueuedTask.Run(()=>{varfirstFeatureLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<ArcGIS.Desktop.Mapping.FeatureLayer>().FirstOrDefault();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the layer inspector.LoadSchema(firstFeatureLayer);// iterate through the attributes, looking at propertiesforeach(var attribute in inspector){varfldName= attribute.FieldName;varfldAlias= attribute.FieldAlias;varfldType= attribute.FieldType;intidxFld= attribute.FieldIndex;varfld= attribute.GetField();varisNullable= attribute.IsNullable;varisEditable= attribute.IsEditable;varisVisible= attribute.IsVisible;varisSystemField= attribute.IsSystemField;varisGeometryField= attribute.IsGeometryField;}});
Read and Write blob fields with the attribute inspector
QueuedTask.Run(()=>{//get selected feature into inspectorvarselectedFeatures= MapView.Active.Map.GetSelection();varinsp=new Inspector(); insp.Load(selectedFeatures.ToDictionary().Keys.First(), selectedFeatures.ToDictionary().Values.First());//read a blob field and save to a filevarmsw=new MemoryStream();msw= insp["Blobfield"]as MemoryStream;using(FileStreamfile=new FileStream(@"d:\temp\blob.jpg", FileMode.Create, FileAccess.Write)){ msw.WriteTo(file);}//read file into memory streamvarmsr=new MemoryStream();using(FileStreamfile=new FileStream(@"d:\images\Hydrant.jpg", FileMode.Open, FileAccess.Read)){ file.CopyTo(msr);}//put the memory stream in the blob field and save to featurevarop=new EditOperation(){Name="Blob Inspector"}; insp["Blobfield"]=msr; op.Modify(insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Read and Write blob fields with a row cursor in a callback
QueuedTask.Run(()=>{vareditOp=new EditOperation(){Name="Blob Cursor"};varfeatLayer= MapView.Active.Map.FindLayers("Hydrant").First()as FeatureLayer; editOp.Callback((context)=>{using(varrc= featLayer.GetTable().Search(null,false)){while(rc.MoveNext()){using(varrecord= rc.Current){//read the blob field and save to a filevarmsw=new MemoryStream();msw= record["BlobField"]as MemoryStream;using(FileStreamfile=new FileStream(@"d:\temp\blob.jpg", FileMode.Create, FileAccess.Write)){ msw.WriteTo(file);}//read file into memory streamvarmsr=new MemoryStream();using(FileStreamfile=new FileStream(@"d:\images\Hydrant.jpg", FileMode.Open, FileAccess.Read)){ file.CopyTo(msr);}//put the memory stream in the blob field and save to feature record["BlobField"]=msr; record.Store();}}}}, featLayer.GetTable());if(!editOp.IsEmpty){varresult= editOp.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Accessing Raster Fields
Read from a raster field
QueuedTask.Run(()=>{varsel= MapView.Active.Map.GetSelection();//Read a raster from a raster field as an InteropBitmap//the bitmap can then be used as an imagesource or written to diskvarinsp=new ArcGIS.Desktop.Editing.Attributes.Inspector(); insp.Load(sel.ToDictionary().Keys.First(), sel.ToDictionary().Values.First());varibmp= insp["Photo"]as System.Windows.Interop.InteropBitmap;});
Write an image to a raster field
QueuedTask.Run(()=>{varsel= MapView.Active.Map.GetSelection();//Insert an image into a raster field//Image will be written with no compressionvarinsp=new ArcGIS.Desktop.Editing.Attributes.Inspector(); insp.Load(sel.ToDictionary().Keys.First(), sel.ToDictionary().Values.First()); insp["Photo"]=@"e:\temp\Hydrant.jpg";varop=new EditOperation(){Name="Raster Inspector"}; op.Modify(insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Write a compressed image to a raster field
QueuedTask.Run(()=>{//Open the raster dataset on disk and create a compressed raster value dataset objectvardataStore=new ArcGIS.Core.Data.FileSystemDatastore(new ArcGIS.Core.Data.FileSystemConnectionPath(new System.Uri(@"e:\temp"), ArcGIS.Core.Data.FileSystemDatastoreType.Raster));using(varfileRasterDataset= dataStore.OpenDataset<ArcGIS.Core.Data.Raster.RasterDataset>("Hydrant.jpg")){varstorageDef=new ArcGIS.Core.Data.Raster.RasterStorageDef(); storageDef.SetCompressionType(ArcGIS.Core.Data.Raster.RasterCompressionType.JPEG); storageDef.SetCompressionQuality(90);varrv=new ArcGIS.Core.Data.Raster.RasterValue(); rv.SetRasterDataset(fileRasterDataset); rv.SetRasterStorageDef(storageDef);varsel= MapView.Active.Map.GetSelection();//insert a raster value object into the raster fieldvarinsp=new ArcGIS.Desktop.Editing.Attributes.Inspector(); insp.Load(sel.ToDictionary().Keys.First(), sel.ToDictionary().Values.First()); insp["Photo"]=rv;varop=new EditOperation(){Name="Raster Inspector"}; op.Modify(insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}}});
Inspector Provider Class
How to create a custom Feature inspector provider class
publicclassMyProvider:InspectorProvider{private System.Guid guid= System.Guid.NewGuid();internalMyProvider(){}publicoverride System.Guid SharedFieldColumnSizeID(){returnguid;}publicoverridestringCustomName(Attributeattr){//Giving a custom name to be displayed for the field FeatureIDif(attr.FieldName =="FeatureID")return"Feature Identification";return attr.FieldName;}publicoverridebool?IsVisible(Attributeattr){//The field FontStyle will not be visibleif(attr.FieldName =="FontStyle")returnfalse;returntrue;}publicoverridebool?IsEditable(Attributeattr){//The field DateField will not be editableif(attr.FieldName =="DateField")returnfalse;returntrue;}publicoverridebool?IsHighlighted(Attributeattr){//ZOrder field will be highlighted in the feature inspector gridif(attr.FieldName =="ZOrder")returntrue;returnfalse;}publicoverrideIEnumerable<Attribute>AttributesOrder(IEnumerable<Attribute>attrs){//Reverse the order of displayvarnewList=newList<Attribute>();foreach(var attr in attrs){
newList.Insert(0, attr);}returnnewList;}publicoverridebool?IsDirty(Attributeattr){//The field will not be marked dirty for FeatureID if you enter the value -1if((attr.FieldName =="FeatureID")&&(attr.CurrentValue.ToString()=="-1"))returnfalse;returnbase.IsDirty(attr);}publicoverrideIEnumerable<ArcGIS.Desktop.Editing.Attributes.Attribute.ValidationError>Validate(Attributeattr){varerrors=newList<ArcGIS.Desktop.Editing.Attributes.Attribute.ValidationError>();if((attr.FieldName =="FeatureID")&&(attr.CurrentValue.ToString()=="2"))
errors.Add(ArcGIS.Desktop.Editing.Attributes.Attribute.ValidationError.Create("Value not allowed", ArcGIS.Desktop.Editing.Attributes.Severity.Low));if((attr.FieldName =="FeatureID")&&(attr.CurrentValue.ToString()=="-1"))
errors.Add(ArcGIS.Desktop.Editing.Attributes.Attribute.ValidationError.Create("Invalid value", ArcGIS.Desktop.Editing.Attributes.Severity.High));returnerrors;}}
Using the custom inspector provider class
varlayer= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetLayersAsFlattenedList().OfType<ArcGIS.Desktop.Mapping.FeatureLayer>().FirstOrDefault();varprovider=new MyProvider();Inspector_featureInspector= provider.Create();//Create an embeddable control from the inspector class to display on the panevaricontrol= _featureInspector.CreateEmbeddableControl();await _featureInspector.LoadAsync(layer, oid);varattribute= _featureInspector.Where(a => a.FieldName =="FontStyle").FirstOrDefault();varvisibility= attribute.IsVisible;//Will return falseattribute= _featureInspector.Where(a => a.FieldName =="ZOrder").FirstOrDefault();varhighlighted= attribute.IsHighlighted;//Will return true
Working with the Sketch
Toggle sketch selection mode
//UseSelection = true; (UseSelection must be set to true in the tool constructor or tool activate)privatebool_inSelMode=false;publicboolIsShiftKey(MapViewKeyEventArgsk){return(k.Key == System.Windows.Input.Key.LeftShift ||
k.Key == System.Windows.Input.Key.RightShift);}protectedoverrideasyncvoidOnToolKeyDown(MapViewKeyEventArgsk){//toggle sketch selection mode with a custom keyif(k.Key == System.Windows.Input.Key.W){if(!_inSelMode){
k.Handled =true;// Toggle the tool to select mode.// The sketch is saved if UseSelection = true;if(await ActivateSelectAsync(true))_inSelMode=true;}}elseif(!_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processing
k.Handled = IsShiftKey(k);}}protectedoverridevoidOnToolKeyUp(MapViewKeyEventArgsk){if(k.Key == System.Windows.Input.Key.W){if(_inSelMode){_inSelMode=false;
k.Handled =true;//process this one// Toggle back to sketch mode. If UseSelection = true// the sketch will be restored
ActivateSelectAsync(false);}}elseif(_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processing
k.Handled = IsShiftKey(k);}}
Listen to the sketch modified event
// SketchModified event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe the sketch modified event//ArcGIS.Desktop.Mapping.Events.SketchModifiedEvent.Subscribe(OnSketchModified);privatevoidOnSketchModified(ArcGIS.Desktop.Mapping.Events.SketchModifiedEventArgs args){// if not an undo operationif(!args.IsUndo){// what was the sketch before the change?varprevSketch= args.PreviousSketch;// what is the current sketch?varcurrentSketch= args.CurrentSketch;if(currentSketch is Polyline polyline){// Examine the current (last) vertex in the line sketchvarlastSketchPoint= polyline.Points.Last();// do something with the last point}}}
Listen to the before sketch completed event and modify the sketch
// BeforeSketchCompleted event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe to the before sketch completed event//ArcGIS.Desktop.Mapping.Events.BeforeSketchCompletedEvent.Subscribe(OnBeforeSketchCompleted);private Task OnBeforeSketchCompleted(BeforeSketchCompletedEventArgsargs){//assign sketch Z values from default surface and set the sketch geometryvarmodifiedSketch= args.MapView.Map.GetZsFromSurfaceAsync(args.Sketch).Result;
args.SetSketchGeometry(modifiedSketch.Geometry);return Task.CompletedTask;}
Listen to the sketch completed event
// SketchCompleted event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe to the sketch completed event//ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Subscribe(OnSketchCompleted);privatevoidOnSketchCompleted(SketchCompletedEventArgsargs){// get the sketchvarfinalSketch= args.Sketch;// do something with the sketch - audit trail perhaps}
Custom construction tool that fires sketch events
internalclassConstructionTool1:MapTool{publicConstructionTool1(){IsSketchTool=true;UseSnapping=true;// Select the type of construction tool you wish to implement. // Make sure that the tool is correctly registered with the correct component category type in the daml SketchType= SketchGeometryType.Line;//Gets or sets whether the sketch is for creating a feature and should use the CurrentTemplate.UsesCurrentTemplate=true;// set FireSketchEvents property to trueFireSketchEvents=true;}// ...}
Customizing the Sketch Symbol of a Custom Sketch Tool
//Custom tools have the ability to change the symbology used when sketching a new feature. //Both the Sketch Segment Symbol and the Vertex Symbol can be modified using the correct set method. //This is set in the activate method for the tool.protectedoverride Task OnToolActivateAsync(boolactive){
QueuedTask.Run(()=>{//Getting the current symbology options of the segmentvarsegmentOptions= GetSketchSegmentSymbolOptions();//Modifying the primary and secondary color and the width of the segment symbology optionsvardeepPurple=new CIMRGBColor(){R=75,G=0,B=110}; segmentOptions.PrimaryColor =deepPurple; segmentOptions.Width =4; segmentOptions.HasSecondaryColor =true;varpink=new CIMRGBColor(){R=219,G=48,B=130}; segmentOptions.SecondaryColor =pink;//Creating a new vertex symbol options instance with the values you wantvarvertexOptions=new VertexSymbolOptions(VertexSymbolType.RegularUnselected);varyellow=new CIMRGBColor(){R=255,G=215,B=0};varpurple=new CIMRGBColor(){R=148,G=0,B=211}; vertexOptions.AngleRotation =45; vertexOptions.Color =yellow; vertexOptions.MarkerType = VertexMarkerType.Star; vertexOptions.OutlineColor =purple; vertexOptions.OutlineWidth =3; vertexOptions.Size =5;//Setting the value of the segment symbol options SetSketchSegmentSymbolOptions(segmentOptions);//Setting the value of the vertex symbol options of the regular unselected vertices using the vertexOptions instance created above. SetSketchVertexSymbolOptions(VertexSymbolType.RegularUnselected, vertexOptions);});returnbase.OnToolActivateAsync(active);}
// 1. Add an embeddable control using VS template. This is the daml entry//<categories>// <updateCategory refID = "esri_embeddableControls">// <insertComponent id="SketchTip_EmbeddableControl1" className="EmbeddableControl1ViewModel">// <content className = "EmbeddableControl1View"/>// </insertComponent>// </updateCategory>// </categories>// 2. Define UI controls on the EmbeddableControl1View// 3. Define properties on the EmbeddableControl1ViewModel which// bind to the UI controls on the EmbeddableControl1ViewpublicclassSketchToolWithUISketchTip:MapTool{publicSketchToolWithUISketchTip(){IsSketchTool=true;SketchType= SketchGeometryType.Line;SketchOutputMode= SketchOutputMode.Map;SketchTipID="SketchTip_EmbeddableControl1";}protectedoverrideTask<bool>OnSketchModifiedAsync(){varsketchTipVM= SketchTipEmbeddableControl as EmbeddableControl1ViewModel;if(sketchTipVM!=null){// modify properties on the sketchTipVM
QueuedTask.Run(async()=>{varsketch=await GetCurrentSketchAsync();varline= sketch as Polyline;varcount= line.PointCount; sketchTipVM.Text ="Vertex Count "+ count.ToString();});}returnbase.OnSketchModifiedAsync();}}
// 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(newList<SnapMode>(){ SnapMode.Point, SnapMode.Edge });// clear all snap modes//At 2.x - ArcGIS.Desktop.Mapping.Snapping.SetSnapModes();
ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(null);// set snap modes one at a time
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Edge,true);
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.End,true);
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Intersection,true);// get current snap modesvarsnapModes= ArcGIS.Desktop.Mapping.Snapping.SnapModes;// get state of a specific snap modeboolisOn= ArcGIS.Desktop.Mapping.Snapping.GetSnapMode(SnapMode.Vertex);
Configure Snapping - Layer Snappability
// is the layer snappable?boolisSnappable= fLayer.IsSnappable;// set snappability for a specific layer - needs to run on the MCTawait QueuedTask.Run(()=>{// use an extension method fLayer.SetSnappable(true);// or use the CIM directly//var layerDef = fLayer.GetDefinition() as ArcGIS.Core.CIM.CIMGeoFeatureLayerBase;//layerDef.Snappable = true;//fLayer.SetDefinition(layerDef);});// turn all layers snappability offlayerList= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();await QueuedTask.Run(()=>{foreach(var layer in layerList){ layer.SetSnappable(false);}});
Configure Snapping - LayerSnapModes
layerList= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();// configure by layerforeach(var layer in layerList){// find the state of the snapModes for the layervarlsm= ArcGIS.Desktop.Mapping.Snapping.GetLayerSnapModes(layer);boolvertexOn= lsm.Vertex;// or use vertexOn= lsm.GetSnapMode(SnapMode.Vertex);booledgeOn= lsm.Edge;// or use edgeOn= lsm.GetSnapMode(SnapMode.Edge);boolendOn= lsm.End;// or use endOn= lsm.GetSnapMode(SnapMode.End);// update a few snapModes // turn Vertex off
lsm.SetSnapMode(SnapMode.Vertex,false);// intersections on
lsm.SetSnapMode(SnapMode.Intersection,true);// and set back to the layer
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer, lsm);// assign a single snap mode at once
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer, SnapMode.Vertex,false);// turn ALL snapModes on
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,true);// turn ALL snapModes off
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,false);}// configure for a set of layers// set Vertex, edge, end on for a set of layers, other snapModes falsevarvee=new LayerSnapModes(false){Vertex=true,Edge=true,End=true};
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layerList, vee);// ensure intersection is on for a set of layers without changing any other snapModes// get the layer snapModes for the set of layersvardictLSM= ArcGIS.Desktop.Mapping.Snapping.GetLayerSnapModes(layerList);foreach(var layer in dictLSM.Keys){varlsm= dictLSM[layer];
lsm.Intersection =true;}
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(dictLSM);// set all snapModes off for a list of layers
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layerList,false);
Configure Snapping - Combined Example
// interested in only snapping to the vertices of a specific layer of interest and not the vertices of other layers// all other snapModes should be off.// snapping must be on
ArcGIS.Desktop.Mapping.Snapping.IsEnabled =true;// turn all application snapModes off//At 2.x - ArcGIS.Desktop.Mapping.Snapping.SetSnapModes();
ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(null);// set application snapMode vertex on
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Vertex,true);// ensure layer snapping is onawait QueuedTask.Run(()=>{ fLayer.SetSnappable(true);});// set vertex snapping onlyvarvertexOnly=new LayerSnapModes(false){Vertex=true};// set vertex only for the specific layer, clearing all othersvardict=newDictionary<Layer,LayerSnapModes>();
dict.Add(fLayer, vertexOnly);
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(dict,true);// true = reset other layers
Snap Options
//Set snapping options via get/set optionsvarsnapOptions= ArcGIS.Desktop.Mapping.Snapping.GetOptions(myMap);//At 2.x - snapOptions.SnapToSketchEnabled = true;
snapOptions.IsSnapToSketchEnabled =true;
snapOptions.XYTolerance =100;//At 2.x - snapOptions.ZToleranceEnabled = true;
snapOptions.IsZToleranceEnabled =true;
snapOptions.ZTolerance =0.6;//turn on snap tip display parts
snapOptions.SnapTipDisplayParts =(int)SnapTipDisplayPart.SnapTipDisplayLayer +(int)SnapTipDisplayPart.SnapTipDisplayType;//turn off all snaptips//snapOptions.SnapTipDisplayParts = (int)SnapTipDisplayPart.SnapTipDisplayNone;//turn on layer display only//snapOptions.SnapTipDisplayParts = (int)SnapTipDisplayPart.SnapTipDisplayLayer;//At 2.x - snapOptions.GeometricFeedbackColor = ColorFactory.Instance.RedRGB;
snapOptions.SnapTipColor = ColorFactory.Instance.RedRGB;
ArcGIS.Desktop.Mapping.Snapping.SetOptions(myMap, snapOptions);
Edit Templates
Find edit template by name on a layer
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//get the templatesvarmap= ArcGIS.Desktop.Mapping.MapView.Active.Map;if(map==null)return;varmainTemplate= map.FindLayers("main").FirstOrDefault()?.GetTemplate("Distribution");varmhTemplate= map.FindLayers("Manhole").FirstOrDefault()?.GetTemplate("Active");});
Find table templates belonging to a standalone table
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{varmap= ArcGIS.Desktop.Mapping.MapView.Active.Map;if(map==null)return;//Get a particular table templatevartableTemplate= map.FindStandaloneTables("Address Points").FirstOrDefault()?.GetTemplate("Residences");//Get all the templates of a standalone tablevarownersTableTemplates= map.FindStandaloneTables("Owners").FirstOrDefault()?.GetTemplates();varstatisticsTableTemplates= MapView.Active.Map.GetStandaloneTablesAsFlattenedList().First(l => l.Name.Equals("Trading Statistics")).GetTemplates();});
Current template
EditingTemplatetemplate= EditingTemplate.Current;
Change Default Edit tool for a template
public Task ChangeTemplateDefaultToolAsync(ArcGIS.Desktop.Mapping.FeatureLayer flayer,stringtoolContentGUID,stringtemplateName){return ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{// retrieve the edit template form the layer by namevartemplate= flayer?.GetTemplate(templateName)as ArcGIS.Desktop.Editing.Templates.EditingTemplate;// get the definition of the layervarlayerDef= flayer?.GetDefinition()as ArcGIS.Core.CIM.CIMFeatureLayer;if((template==null)||(layerDef==null))return;if(template.DefaultToolID !=this.ID){boolupdateLayerDef=false;if(layerDef.AutoGenerateFeatureTemplates){ layerDef.AutoGenerateFeatureTemplates =false;updateLayerDef=true;}// retrieve the CIM edit template definitionvartemplateDef= template.GetDefinition();// assign the GUID from the tool DAML definition, for example// <tool id="TestConstructionTool_SampleSDKTool" categoryRefID="esri_editing_construction_polyline" ….>// <tooltip heading="">Tooltip text<disabledText /></tooltip>// <content guid="e58239b3-9c69-49e5-ad4d-bb2ba29ff3ea" />// </tool>// then the toolContentGUID would be "e58239b3-9c69-49e5-ad4d-bb2ba29ff3ea"//At 2.x -//templateDef.ToolProgID = toolContentGUID; templateDef.DefaultToolGUID =toolContentGUID;// set the definition back to template.SetDefinition(templateDef);// update the layer definition tooif(updateLayerDef) flayer.SetDefinition(layerDef);}});}
Hide or show editing tools on templates
QueuedTask.Run(()=>{//hide all tools except line tool on layervarfeatLayer= MapView.Active.Map.FindLayers("Roads").First();vareditTemplates= featLayer.GetTemplates();varnewCIMEditingTemplates=newList<CIMEditingTemplate>();foreach(var et in editTemplates){//initialize template by activating default tool et.ActivateDefaultToolAsync();varcimEditTemplate= et.GetDefinition();//get the visible tools on this templatevarallTools= et.ToolIDs.ToList();//add the hidden tools on this template allTools.AddRange(cimEditTemplate.GetExcludedToolIDs().ToList());//hide all the tools then allow the line tool//At 2.x -//allTools.AddRange(cimEditTemplate.GetExcludedToolDamlIds().ToList()); allTools.AddRange(cimEditTemplate.GetExcludedToolIDs().ToList());//At 2.x - //cimEditTemplate.SetExcludedToolDamlIds(allTools.ToArray());//cimEditTemplate.AllowToolDamlID("esri_editing_SketchLineTool"); cimEditTemplate.SetExcludedToolIDs(allTools.ToArray()); cimEditTemplate.AllowToolID("esri_editing_SketchLineTool"); newCIMEditingTemplates.Add(cimEditTemplate);}//update the layer templatesvarlayerDef= featLayer.GetDefinition()as CIMFeatureLayer;// Set AutoGenerateFeatureTemplates to false for template changes to stick layerDef.AutoGenerateFeatureTemplates =false; layerDef.FeatureTemplates = newCIMEditingTemplates.ToArray(); featLayer.SetDefinition(layerDef);});
Create New Template using layer.CreateTemplate
varlayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault();if(layer==null)return;
QueuedTask.Run(()=>{varinsp=new Inspector(); insp.LoadSchema(layer); insp["Field1"]=value1; insp["Field2"]=value2; insp["Field3"]=value3;vartags=new[]{"Polygon","tag1","tag2"};// set defaultTool using a daml-id stringdefaultTool="esri_editing_SketchCirclePolygonTool";// tool filter is the tools to filter OUTvartoolFilter=new[]{"esri_editing_SketchTracePolygonTool"};// create a new template varnewTemplate= layer.CreateTemplate("My new template","description", insp, defaultTool, tags, toolFilter);});
Create New Table Template using table.CreateTemplate
vartable= MapView.Active.Map.GetStandaloneTablesAsFlattenedList().FirstOrDefault();if(table==null)return;
QueuedTask.Run(()=>{vartableTemplate= table.GetTemplate("Template1");vardefinition= tableTemplate.GetDefinition(); definition.Description ="New definition"; definition.Name ="New name";//Create new table template using this definition table.CreateTemplate(definition);//You can also create a new table template using this extension method. You can use this method the same way you use the layer.CreateTemplate method. table.CreateTemplate("New template name","Template description", tags:newstring[]{"tag 1","tag 2"});});
// get an anno layerAnnotationLayerannoLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<AnnotationLayer>().FirstOrDefault();if(annoLayer==null)return;
QueuedTask.Run(()=>{Inspectorinsp=null;// get the anno feature classvarfc= annoLayer.GetFeatureClass()as ArcGIS.Core.Data.Mapping.AnnotationFeatureClass;// get the featureclass CIM definition which contains the labels, symbolsvarcimDefinition= fc.GetDefinition()as ArcGIS.Core.Data.Mapping.AnnotationFeatureClassDefinition;varlabels= cimDefinition.GetLabelClassCollection();varsymbols= cimDefinition.GetSymbolCollection();// make sure there are labels, symbolsif((labels.Count ==0)||(symbols.Count ==0))return;// find the label class required// typically you would use a subtype name or some other characteristic// in this case lets just use the first onevarlabel= labels[0];// each label has a textSymbol// the symbolName *should* be the symbolID to be usedvarsymbolName= label.TextSymbol.SymbolName;intsymbolID=-1;if(!int.TryParse(symbolName,out symbolID)){// int.TryParse fails - attempt to find the symbolName in the symbol collectionforeach(var symbol in symbols){if(symbol.Name ==symbolName){symbolID= symbol.ID;break;}}}// no symbol?if(symbolID==-1)return;// load the schemainsp=new Inspector(); insp.LoadSchema(annoLayer);// ok to assign these fields using the inspector[fieldName] methodology// these fields are guaranteed to exist in the annotation schema insp["AnnotationClassID"]= label.ID; insp["SymbolID"]=symbolID;// set up some additional annotation propertiesAnnotationPropertiesannoProperties= insp.GetAnnotationProperties(); annoProperties.FontSize =36; annoProperties.TextString ="My Annotation feature"; annoProperties.VerticalAlignment = VerticalAlignment.Top; annoProperties.HorizontalAlignment = HorizontalAlignment.Justify; insp.SetAnnotationProperties(annoProperties);vartags=new[]{"Annotation","tag1","tag2"};// use daml-id rather than guidstringdefaultTool="esri_editing_SketchStraightAnnoTool";// tool filter is the tools to filter OUTvartoolFilter=new[]{"esri_editing_SketchCurvedAnnoTool"};// create a new template varnewTemplate= annoLayer.CreateTemplate("new anno template","description", insp, defaultTool, tags, toolFilter);});
Remove a table template
vartable= MapView.Active.Map.GetStandaloneTablesAsFlattenedList().FirstOrDefault();if(table==null)return;
QueuedTask.Run(()=>{vartableTemplate= table.GetTemplate("Template1");//Removing a table template table.RemoveTemplate(tableTemplate);//Removing a template by name table.RemoveTemplate("Template2");});
Active Template Changed
ArcGIS.Desktop.Editing.Events.ActiveTemplateChangedEvent.Subscribe(OnActiveTemplateChanged);asyncvoidOnActiveTemplateChanged(ArcGIS.Desktop.Editing.Events.ActiveTemplateChangedEventArgs args){// return if incoming template is nullif(args.IncomingTemplate ==null)return;// Activate two-point line tool for Freeway template in the Layers mapif(args.IncomingTemplate.Name =="Freeway"&& args.IncomingMapView.Map.Name =="Layers")await args.IncomingTemplate.ActivateToolAsync("esri_editing_SketchTwoPointLineTool");}
Annotation
Annotation Construction Tool
//In your config.daml...set the categoryRefID//<tool id="..." categoryRefID="esri_editing_construction_annotation" caption="Create Anno" ...>//Sketch type Point or Line or BezierLine in the constructor...//internal class AnnoConstructionTool : MapTool {// public AnnoConstructionTool() {// IsSketchTool = true;// UseSnapping = true;// SketchType = SketchGeometryType.Point;//protectedasyncoverrideTask<bool>OnSketchCompleteAsync(Geometrygeometry){if(CurrentTemplate==null||geometry==null)returnfalse;// Create an edit operationvarcreateOperation=new EditOperation();
createOperation.Name =string.Format("Create {0}", CurrentTemplate.Layer.Name);
createOperation.SelectNewFeatures =true;varinsp= CurrentTemplate.Inspector;varresult=await QueuedTask.Run(()=>{// get the annotation properties classAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// set custom annotation properties annoProperties.TextString ="my custom text"; annoProperties.Color = ColorFactory.Instance.RedRGB; annoProperties.FontSize =24; annoProperties.FontName ="Arial"; annoProperties.HorizontalAlignment = ArcGIS.Core.CIM.HorizontalAlignment.Right; annoProperties.Shape =geometry;// assign annotation properties back to the inspector insp.SetAnnotationProperties(annoProperties);// Queue feature creation createOperation.Create(CurrentTemplate.Layer, insp);if(!createOperation.IsEmpty){// Execute the operationreturn createOperation.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}elsereturnfalse;});returnresult;}
await QueuedTask.Run(()=>{// annoLayer is ~your~ Annotation layer...// pnt is ~your~ Annotation geometry ...varop=new EditOperation();// Use the inspectorvarinsp=new Inspector(); insp.LoadSchema(annoLayer);// get the annotation properties from the inspectorAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// change the annotation text annoProperties.TextString = DateTime.Now.ToLongTimeString();// change font color to green annoProperties.Color = ColorFactory.Instance.GreenRGB;// change the horizontal alignment annoProperties.HorizontalAlignment = HorizontalAlignment.Center; annoProperties.Shape =pnt;// set the annotation properties back on the inspector insp.SetAnnotationProperties(annoProperties);// create the annotation op.Create(annoLayer, insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Update Annotation Text
await QueuedTask.Run(()=>{//annoLayer is ~your~ Annotation layer...// use the inspector methodology//at 2.x - var insp = new Inspector(true);varinsp=new Inspector(); insp.Load(annoLayer, oid);// get the annotation propertiesAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// set the attribute annoProperties.TextString ="Hello World";// assign the annotation proeprties back to the inspector insp.SetAnnotationProperties(annoProperties);//create and execute the edit operationEditOperationop=new EditOperation(); op.Name ="Update annotation"; op.Modify(insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Modify Annotation Shape
await QueuedTask.Run(()=>{//Don't use 'Shape'....Shape is the bounding box of the annotation text. This is NOT what you want...////var insp = new Inspector();//insp.Load(annoLayer, oid);//var shape = insp["SHAPE"] as Polygon;//...wrong shape...//Instead, we must use the AnnotationProperties//annoLayer is ~your~ Annotation layer//at 2.x - var insp = new Inspector(true);varinsp=new Inspector(); insp.Load(annoLayer, oid);AnnotationPropertiesannoProperties= insp.GetAnnotationProperties();varshape= annoProperties.Shape;if(shape.GeometryType != GeometryType.GeometryBag){varnewGeometry= GeometryEngine.Instance.Move(shape,10,10); annoProperties.Shape =newGeometry; insp.SetAnnotationProperties(annoProperties);EditOperationop=new EditOperation(); op.Name ="Change annotation angle"; op.Modify(insp);if(!op.IsEmpty){varresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}}});
Modify Annotation Text Graphic
await QueuedTask.Run(()=>{varselection= annoLayer.GetSelection();if(selection.GetCount()==0)return;// use the first selelcted feature //at 2.x - var insp = new Inspector(true);varinsp=new Inspector(); insp.Load(annoLayer, selection.GetObjectIDs().FirstOrDefault());// getAnnoProperties should return null if not an annotation featureAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// get the textGraphicCIMTextGraphictextGraphic= annoProperties.TextGraphic;// change text textGraphic.Text ="Hello world";// set x,y offset via the symbolvarsymbol= textGraphic.Symbol.Symbol;vartextSymbol= symbol as CIMTextSymbol; textSymbol.OffsetX =2; textSymbol.OffsetY =3; textSymbol.HorizontalAlignment = HorizontalAlignment.Center;// load the updated textGraphic annoProperties.LoadFromTextGraphic(textGraphic);// assign the annotation properties back insp.SetAnnotationProperties(annoProperties);EditOperationop=new EditOperation(); op.Name ="modify symbol"; op.Modify(insp);if(!op.IsEmpty){boolresult= op.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}});
Undo / Redo
Undo/Redo the Most Recent Operation
//undoif(MapView.Active.Map.OperationManager.CanUndo)
MapView.Active.Map.OperationManager.UndoAsync();//await as needed//redoif(MapView.Active.Map.OperationManager.CanRedo)
MapView.Active.Map.OperationManager.RedoAsync();//await as needed
Topology Properties
Get List of available topologies in the map
QueuedTask.Run(async()=>{varmap= MapView.Active.Map;//Get a list of all the available topologies for the mapvaravailableTopologies=await map.GetAvailableTopologiesAsync();vargdbTopologies= availableTopologies.OfType<GeodatabaseTopologyProperties>();varmapTopologies= availableTopologies.OfType<MapTopologyProperties>();});
Get the properties of the active topology in the map
varmap= MapView.Active.Map;varactiveTopologyProperties=await map.GetActiveTopologyAsync();varisMapTopology= activeTopologyProperties is MapTopologyProperties;varisGdbTopology= activeTopologyProperties is GeodatabaseTopologyProperties;varisNoTopology= activeTopologyProperties is NoTopologyProperties;
privateasync Task BuildGraphWithActiveView(){await QueuedTask.Run(()=>{//Build the map topology graph MapView.Active.BuildMapTopologyGraph<TopologyDefinition>(async topologyGraph =>{//Getting the nodes and edges present in the graphvartopologyGraphNodes= topologyGraph.GetNodes();vartopologyGraphEdges= topologyGraph.GetEdges();foreach(var node in topologyGraphNodes){// do something with the node}foreach(var edge in topologyGraphEdges){// do something with the edge} MessageBox.Show($"Number of topo graph nodes are: {topologyGraphNodes.Count}.\n Number of topo graph edges are {topologyGraphEdges.Count}.","Map Topology Info");});});}
Attributes Pane Context MenuItems
Retrieve SelectionSet from command added to Attribute Pane Context Menu
varoptions= ApplicationOptions.EditingOptions;//Must use QueuedTask
QueuedTask.Run(()=>{//There are 4 vertex symbol settings - selected, unselected and the//current vertex selected and unselected.varreg_select= options.GetVertexSymbolOptions(VertexSymbolType.RegularSelected);varreg_unsel= options.GetVertexSymbolOptions(VertexSymbolType.RegularUnselected);varcurr_sel= options.GetVertexSymbolOptions(VertexSymbolType.CurrentSelected);varcurr_unsel= options.GetVertexSymbolOptions(VertexSymbolType.CurrentUnselected);//to convert the options to a symbol use//GetPointSymbolvarreg_sel_pt_symbol= reg_select.GetPointSymbol();//ditto for reg_unsel, curr_sel, curr_unsel});
Get Sketch Segment Symbology Options
//var options = ApplicationOptions.EditingOptions;
QueuedTask.Run(()=>{varseg_options= options.GetSegmentSymbolOptions();//to convert the options to a symbol use//SymbolFactory. Note: this is approximate....sketch isn't using the//CIM directly for segmentsvarlayers=newList<CIMSymbolLayer>();varstroke0= SymbolFactory.Instance.ConstructStroke(seg_options.PrimaryColor, seg_options.Width, SimpleLineStyle.Dash); layers.Add(stroke0);if(seg_options.HasSecondaryColor){varstroke1= SymbolFactory.Instance.ConstructStroke( seg_options.SecondaryColor, seg_options.Width, SimpleLineStyle.Solid); layers.Add(stroke1);}//segment symbology onlyvarsketch_line=new CIMLineSymbol(){SymbolLayers= layers.ToArray()};});
Set Sketch Vertex Symbol Options
//var options = ApplicationOptions.EditingOptions;
QueuedTask.Run(()=>{//change the regular unselected vertex symbol//default is a green, hollow, square, 5pts. Change to//Blue outline diamond, 10 ptsvarvertexSymbol=new VertexSymbolOptions(VertexSymbolType.RegularUnselected); vertexSymbol.OutlineColor = ColorFactory.Instance.BlueRGB; vertexSymbol.MarkerType = VertexMarkerType.Diamond; vertexSymbol.Size =10;//Are these valid?if(options.CanSetVertexSymbolOptions( VertexSymbolType.RegularUnselected, vertexSymbol)){//apply them options.SetVertexSymbolOptions(VertexSymbolType.RegularUnselected, vertexSymbol);}});
Set Sketch Segment Symbol Options
//var options = ApplicationOptions.EditingOptions;
QueuedTask.Run(()=>{//change the segment symbol primary color to green and//width to 1 ptvarsegSymbol= options.GetSegmentSymbolOptions(); segSymbol.PrimaryColor = ColorFactory.Instance.GreenRGB; segSymbol.Width =1;//Are these valid?if(options.CanSetSegmentSymbolOptions(segSymbol)){//apply them options.SetSegmentSymbolOptions(segSymbol);}});
Set Sketch Vertex Symbol Back to Default
//var options = ApplicationOptions.EditingOptions;
QueuedTask.Run(()=>{//ditto for reg selected and current selected, unselectedvardef_reg_unsel= options.GetDefaultVertexSymbolOptions(VertexSymbolType.RegularUnselected);//apply default options.SetVertexSymbolOptions( VertexSymbolType.RegularUnselected, def_reg_unsel);});