// 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=newEditOperation(){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 MapToolcreateFeatures.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=newArcGIS.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=newArcGIS.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=newEditOperation(){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 MCTArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//Create the edit operationvarcreateOperation=newArcGIS.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(varitemincsvData){//Create the point geometryArcGIS.Core.Geometry.MapPointnewMapPoint=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 creationcreateOperation.Create(layer,atts);}// execute the edit (feature creation) operationif(createOperation.IsEmpty){returncreateOperation.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=newEditOperation(){Name="Create a row in a table"};//Creating a new row in a standalone table using the table template of your choicecreateRow.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=newEditOperation(){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=newEditOperation(){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=newEditOperation(){Name="Delete Features"};vartable=MapView.Active.Map.StandaloneTables[0];//Delete a row in a standalone tabledeleteFeatures.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=newEditOperation(){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=newInspector();insp2.Load(featureLayer,oid);vargeom=insp2["SHAPE"]asGeometry;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=newEditOperation(){Name="Explode Features"};//Take a multipart and convert it into one feature per part//Provide a list of ids to convert multipleexplodeFeatures.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=newEditOperation(){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.CurrentTemplateasEditingRowTemplate,featureLayer,newList<long>(){10,96,12});//Merge three features into a new feature in the destination layermergeFeatures.Merge(destinationLayer,featureLayer,newList<long>(){10,96,12});//Use an inspector to set the new attributes of the merged featurevarinspector=newInspector();inspector.Load(featureLayer,oid);//base attributes on an existing feature//change attributes for the new featureinspector["NAME"]="New name";inspector["DESCRIPTION"]="New description";//Merge features into a new feature in the same layer using the//defaults set in the inspectormergeFeatures.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=newEditOperation(){Name="Modify a feature"};//use an inspectorvarmodifyInspector=newInspector();modifyInspector.Load(featureLayer,oid);//base attributes on an existing feature//change attributes for the new featuremodifyInspector["SHAPE"]=polygon;//Update the geometrymodifyInspector["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=newQueryFilter(){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=newEditOperation(){Name="Modify features"};modifyFeatures.ShowProgressor=true;varmultipleFeaturesInsp=newInspector();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()asBasicFeatureLayer;//Search by attributevarfilter=newArcGIS.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=newEditOperation(){Name="Update date"};// load features into inspector and update fieldvardateInsp=newArcGIS.Desktop.Editing.Attributes.Inspector();dateInsp.Load(disLayer,oids);dateInsp["InspDate"]="9/21/2013";// modify and executemodifyOp.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(firstLayerasMapMember,selectionfromMap.GetObjectIDs().ToList());varmoveFeature=newEditOperation(){Name="Move features"};//at 2.x - moveFeature.Move(selectionDictionary, 10, 10); //specify your units along axis to move the geometrymoveFeature.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=newMapPointBuilderEx(1.0,2.0,3.0,4.0,MapView.Active.Map.SpatialReference);//can pass in coordinates.varmodifyFeatureCoord=newEditOperation(){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=newEditOperation(){Name="Planarize Features"};//Planarize one or more featuresplanarizeFeatures.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=newParallelOffset.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=newEditOperation();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=newEditOperation(){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=newEditOperation(){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=newEditOperation(){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 directionscaleFeatures.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=newEditOperation(){Name="Split Features"};varsplitPoints=newList<MapPoint>(){mp1,mp2,mp3};//Split the feature at 3 pointssplitFeatures.Split(featureLayer,oid,splitPoints);// split using percentagevarsplitByPercentage=newSplitByPercentage(){Percentage=33,SplitFromStartPoint=true};splitFeatures.Split(featureLayer,oid,splitByPercentage);// split using equal partsvarsplitByEqualParts=newSplitByEqualParts(){NumParts=3};splitFeatures.Split(featureLayer,oid,splitByEqualParts);// split using single distancevarsplitByDistance=newSplitByDistance(){Distance=27.3,SplitFromStartPoint=false};splitFeatures.Split(featureLayer,oid,splitByDistance);// split using varying distancevardistances=newList<double>(){12.5,38.2,89.99};varsplitByVaryingDistance=newSplitByVaryingDistance(){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=newEditOperation(){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=newTransformByLinkLayer(){LinkLayer=linkLayer,TransformType=TransformMethodType.Affine//TransformMethodType.Similarity};//Transform a selected set of featurestransformFeatures.Transform(MapView.Active.GetFeatures(polygon),affine_transform);//Perform an affine transformationtransformFeatures.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=newRubbersheetByGeometries(){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=newEditOperation();//Performs linear rubbersheet transformation on the features belonging to "layer" that fall within the limited adjustment areasrubbersheetOp.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=newRubbersheetByLayers(){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 areasrubbersheetOp.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=newEditOperation(){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=newEditOperation(){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 QueuedTaskeditOperation1.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 ideditOperation2.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 transactionseditOperation2.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=newEditOperation();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=newQueryFilter(){WhereClause="OBJECTID = "+oid.ToString()};// create an edit operation and name.varop=newEditOperation(){Name="modify followed by split"};// set the ExecuteModeop.ExecuteMode=ExecuteModeType.Sequential;using(varrowCursor=fc.Search(queryFilter,false)){while(rowCursor.MoveNext()){using(varfeature=rowCursor.CurrentasFeature){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=newInspector();testInspector.Load(selectedFeatures.ToDictionary().Keys.First(),selectedFeatures.ToDictionary().Values.First());testInspector["Name"]="test";//create and execute the edit operationvarupdateTestField=newEditOperation(){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.CurrentasFeature;varline=lineFeature.GetShape()asPolyline;intvertexIndex=1;oid=lineFeature.GetObjectID();//Each point is converted into a MapPoint and gets added to a list. foreach(varpointinline.Points){MapPointBuilderExmapPointBuilderEx=newMapPointBuilderEx(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()asMapPoint);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()asFeatureLayer;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=newGeodatabase(newFileGeodatabaseConnectionPath(newUri(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� valuesargs.CancelEdit($"Police district {row["POLICE_DISTRICT"]} is invalid");}}// update the description fieldrow["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 changeparentEditOp.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)asGeometry;//New geometry of the modified rowvargeomNew=args.Row[shapeIndex]asGeometry;//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);}protectedTaskOnEce(EditCompletedEventArgsargs){//show number of editsConsole.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());returnTask.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=newArcGIS.Desktop.Editing.Attributes.Inspector();// load the feature with ObjectID 'oid' into the inspectorawaitinspector.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=newArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawaitinspector.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=newArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsinspector.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=newArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawaitinspector.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 featuresinspector["Description"]="The new value.";// apply the changes as an edit operation awaitinspector.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=newArcGIS.Desktop.Editing.Attributes.Inspector();// load the layerinspector.LoadSchema(firstFeatureLayer);// iterate through the attributes, looking at propertiesforeach(varattributeininspector){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=newInspector();insp.Load(selectedFeatures.ToDictionary().Keys.First(),selectedFeatures.ToDictionary().Values.First());//read a blob field and save to a filevarmsw=newMemoryStream();msw=insp["Blobfield"]asMemoryStream;using(FileStreamfile=newFileStream(@"d:\temp\blob.jpg",FileMode.Create,FileAccess.Write)){msw.WriteTo(file);}//read file into memory streamvarmsr=newMemoryStream();using(FileStreamfile=newFileStream(@"d:\images\Hydrant.jpg",FileMode.Open,FileAccess.Read)){file.CopyTo(msr);}//put the memory stream in the blob field and save to featurevarop=newEditOperation(){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=newEditOperation(){Name="Blob Cursor"};varfeatLayer=MapView.Active.Map.FindLayers("Hydrant").First()asFeatureLayer;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=newMemoryStream();msw=record["BlobField"]asMemoryStream;using(FileStreamfile=newFileStream(@"d:\temp\blob.jpg",FileMode.Create,FileAccess.Write)){msw.WriteTo(file);}//read file into memory streamvarmsr=newMemoryStream();using(FileStreamfile=newFileStream(@"d:\images\Hydrant.jpg",FileMode.Open,FileAccess.Read)){file.CopyTo(msr);}//put the memory stream in the blob field and save to featurerecord["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=newArcGIS.Desktop.Editing.Attributes.Inspector();insp.Load(sel.ToDictionary().Keys.First(),sel.ToDictionary().Values.First());varibmp=insp["Photo"]asSystem.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=newArcGIS.Desktop.Editing.Attributes.Inspector();insp.Load(sel.ToDictionary().Keys.First(),sel.ToDictionary().Values.First());insp["Photo"]=@"e:\temp\Hydrant.jpg";varop=newEditOperation(){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=newArcGIS.Core.Data.FileSystemDatastore(newArcGIS.Core.Data.FileSystemConnectionPath(newSystem.Uri(@"e:\temp"),ArcGIS.Core.Data.FileSystemDatastoreType.Raster));using(varfileRasterDataset=dataStore.OpenDataset<ArcGIS.Core.Data.Raster.RasterDataset>("Hydrant.jpg")){varstorageDef=newArcGIS.Core.Data.Raster.RasterStorageDef();storageDef.SetCompressionType(ArcGIS.Core.Data.Raster.RasterCompressionType.JPEG);storageDef.SetCompressionQuality(90);varrv=newArcGIS.Core.Data.Raster.RasterValue();rv.SetRasterDataset(fileRasterDataset);rv.SetRasterStorageDef(storageDef);varsel=MapView.Active.Map.GetSelection();//insert a raster value object into the raster fieldvarinsp=newArcGIS.Desktop.Editing.Attributes.Inspector();insp.Load(sel.ToDictionary().Keys.First(),sel.ToDictionary().Values.First());insp["Photo"]=rv;varop=newEditOperation(){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{privateSystem.Guidguid=System.Guid.NewGuid();internalMyProvider(){}publicoverrideSystem.GuidSharedFieldColumnSizeID(){returnguid;}publicoverridestringCustomName(Attributeattr){//Giving a custom name to be displayed for the field FeatureIDif(attr.FieldName=="FeatureID")return"Feature Identification";returnattr.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(varattrinattrs){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=newMyProvider();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(awaitActivateSelectAsync(true))_inSelMode=true;}}elseif(!_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processingk.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 restoredActivateSelectAsync(false);}}elseif(_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processingk.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.SketchModifiedEventArgsargs){// 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(currentSketchisPolylinepolyline){// 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);privateTaskOnBeforeSketchCompleted(BeforeSketchCompletedEventArgsargs){//assign sketch Z values from default surface and set the sketch geometryvarmodifiedSketch=args.MapView.Map.GetZsFromSurfaceAsync(args.Sketch).Result;args.SetSketchGeometry(modifiedSketch.Geometry);returnTask.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.protectedoverrideTaskOnToolActivateAsync(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=newCIMRGBColor(){R=75,G=0,B=110};segmentOptions.PrimaryColor=deepPurple;segmentOptions.Width=4;segmentOptions.HasSecondaryColor=true;varpink=newCIMRGBColor(){R=219,G=48,B=130};segmentOptions.SecondaryColor=pink;//Creating a new vertex symbol options instance with the values you wantvarvertexOptions=newVertexSymbolOptions(VertexSymbolType.RegularUnselected);varyellow=newCIMRGBColor(){R=255,G=215,B=0};varpurple=newCIMRGBColor(){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 optionsSetSketchSegmentSymbolOptions(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=SketchTipEmbeddableControlasEmbeddableControl1ViewModel;if(sketchTipVM!=null){// modify properties on the sketchTipVMQueuedTask.Run(async()=>{varsketch=awaitGetCurrentSketchAsync();varline=sketchasPolyline;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 timeArcGIS.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 MCTawaitQueuedTask.Run(()=>{// use an extension methodfLayer.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>();awaitQueuedTask.Run(()=>{foreach(varlayerinlayerList){layer.SetSnappable(false);}});
Configure Snapping - LayerSnapModes
layerList=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();// configure by layerforeach(varlayerinlayerList){// 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 offlsm.SetSnapMode(SnapMode.Vertex,false);// intersections onlsm.SetSnapMode(SnapMode.Intersection,true);// and set back to the layerArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,lsm);// assign a single snap mode at onceArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,SnapMode.Vertex,false);// turn ALL snapModes onArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,true);// turn ALL snapModes offArcGIS.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=newLayerSnapModes(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(varlayerindictLSM.Keys){varlsm=dictLSM[layer];lsm.Intersection=true;}ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(dictLSM);// set all snapModes off for a list of layersArcGIS.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 onArcGIS.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 onawaitQueuedTask.Run(()=>{fLayer.SetSnappable(true);});// set vertex snapping onlyvarvertexOnly=newLayerSnapModes(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 partssnapOptions.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
publicTaskChangeTemplateDefaultToolAsync(ArcGIS.Desktop.Mapping.FeatureLayerflayer,stringtoolContentGUID,stringtemplateName){returnArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{// retrieve the edit template form the layer by namevartemplate=flayer?.GetTemplate(templateName)asArcGIS.Desktop.Editing.Templates.EditingTemplate;// get the definition of the layervarlayerDef=flayer?.GetDefinition()asArcGIS.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(varetineditTemplates){//initialize template by activating default toolet.ActivateDefaultToolAsync();varcimEditTemplate=et.GetDefinition();//get the visible tools on this templatevarallTools=et.ToolIDs.ToList();//add the hidden tools on this templateallTools.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()asCIMFeatureLayer;// Set AutoGenerateFeatureTemplates to false for template changes to sticklayerDef.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=newInspector();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 definitiontable.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"});});
Update a Table Template
QueuedTask.Run(()=>{vartableTemplate=table.GetTemplate("Template1");vardefinition=tableTemplate.GetDefinition();definition.Description="New definition";definition.Name="New name";// update the definitiontableTemplate.SetDefinition(definition);});
Create Annotation Template
// 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()asArcGIS.Core.Data.Mapping.AnnotationFeatureClass;// get the featureclass CIM definition which contains the labels, symbolsvarcimDefinition=fc.GetDefinition()asArcGIS.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,outsymbolID)){// int.TryParse fails - attempt to find the symbolName in the symbol collectionforeach(varsymbolinsymbols){if(symbol.Name==symbolName){symbolID=symbol.ID;break;}}}// no symbol?if(symbolID==-1)return;// load the schemainsp=newInspector();insp.LoadSchema(annoLayer);// ok to assign these fields using the inspector[fieldName] methodology// these fields are guaranteed to exist in the annotation schemainsp["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 templatetable.RemoveTemplate(tableTemplate);//Removing a template by nametable.RemoveTemplate("Template2");});
Active Template Changed
ArcGIS.Desktop.Editing.Events.ActiveTemplateChangedEvent.Subscribe(OnActiveTemplateChanged);asyncvoidOnActiveTemplateChanged(ArcGIS.Desktop.Editing.Events.ActiveTemplateChangedEventArgsargs){// 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")awaitargs.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=newEditOperation();createOperation.Name=string.Format("Create {0}",CurrentTemplate.Layer.Name);createOperation.SelectNewFeatures=true;varinsp=CurrentTemplate.Inspector;varresult=awaitQueuedTask.Run(()=>{// get the annotation properties classAnnotationPropertiesannoProperties=insp.GetAnnotationProperties();// set custom annotation propertiesannoProperties.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 inspectorinsp.SetAnnotationProperties(annoProperties);// Queue feature creationcreateOperation.Create(CurrentTemplate.Layer,insp);if(!createOperation.IsEmpty){// Execute the operationreturncreateOperation.Execute();//Execute and ExecuteAsync will return true if the operation was successful and false if not}elsereturnfalse;});returnresult;}
awaitQueuedTask.Run(()=>{// annoLayer is ~your~ Annotation layer...// pnt is ~your~ Annotation geometry ...varop=newEditOperation();// Use the inspectorvarinsp=newInspector();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 greenannoProperties.Color=ColorFactory.Instance.GreenRGB;// change the horizontal alignmentannoProperties.HorizontalAlignment=HorizontalAlignment.Center;annoProperties.Shape=pnt;// set the annotation properties back on the inspectorinsp.SetAnnotationProperties(annoProperties);// create the annotationop.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
awaitQueuedTask.Run(()=>{//annoLayer is ~your~ Annotation layer...// use the inspector methodology//at 2.x - var insp = new Inspector(true);varinsp=newInspector();insp.Load(annoLayer,oid);// get the annotation propertiesAnnotationPropertiesannoProperties=insp.GetAnnotationProperties();// set the attributeannoProperties.TextString="Hello World";// assign the annotation proeprties back to the inspectorinsp.SetAnnotationProperties(annoProperties);//create and execute the edit operationEditOperationop=newEditOperation();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
awaitQueuedTask.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=newInspector();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=newEditOperation();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
awaitQueuedTask.Run(()=>{varselection=annoLayer.GetSelection();if(selection.GetCount()==0)return;// use the first selelcted feature //at 2.x - var insp = new Inspector(true);varinsp=newInspector();insp.Load(annoLayer,selection.GetObjectIDs().FirstOrDefault());// getAnnoProperties should return null if not an annotation featureAnnotationPropertiesannoProperties=insp.GetAnnotationProperties();// get the textGraphicCIMTextGraphictextGraphic=annoProperties.TextGraphic;// change texttextGraphic.Text="Hello world";// set x,y offset via the symbolvarsymbol=textGraphic.Symbol.Symbol;vartextSymbol=symbolasCIMTextSymbol;textSymbol.OffsetX=2;textSymbol.OffsetY=3;textSymbol.HorizontalAlignment=HorizontalAlignment.Center;// load the updated textGraphicannoProperties.LoadFromTextGraphic(textGraphic);// assign the annotation properties back insp.SetAnnotationProperties(annoProperties);EditOperationop=newEditOperation();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=awaitmap.GetAvailableTopologiesAsync();vargdbTopologies=availableTopologies.OfType<GeodatabaseTopologyProperties>();varmapTopologies=availableTopologies.OfType<MapTopologyProperties>();});
Get the properties of the active topology in the map
privateasyncTaskBuildGraphWithActiveView(){awaitQueuedTask.Run(()=>{//Build the map topology graphMapView.Active.BuildMapTopologyGraph<TopologyDefinition>(async topologyGraph =>{//Getting the nodes and edges present in the graphvartopologyGraphNodes=topologyGraph.GetNodes();vartopologyGraphEdges=topologyGraph.GetEdges();foreach(varnodeintopologyGraphNodes){// do something with the node}foreach(varedgeintopologyGraphEdges){// 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 QueuedTaskQueuedTask.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=newCIMLineSymbol(){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=newVertexSymbolOptions(VertexSymbolType.RegularUnselected);vertexSymbol.OutlineColor=ColorFactory.Instance.BlueRGB;vertexSymbol.MarkerType=VertexMarkerType.Diamond;vertexSymbol.Size=10;//Are these valid?if(options.CanSetVertexSymbolOptions(VertexSymbolType.RegularUnselected,vertexSymbol)){//apply themoptions.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 themoptions.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 defaultoptions.SetVertexSymbolOptions(VertexSymbolType.RegularUnselected,def_reg_unsel);});