varcreateFeatures=newEditOperation();createFeatures.Name="Create Features";//Create a feature with a polygoncreateFeatures.Create(featureLayer,polygon);//with a callbackcreateFeatures.Create(featureLayer,polygon,(object_id)=>{//TODO - use the oid of the created feature//in your callback});//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.RuncreateFeatures.Execute();//or use async flavor//await createFeatures.ExecuteAsync();
Create a feature using the current template
varmyTemplate=ArcGIS.Desktop.Editing.Templates.EditingTemplate.Current;varmyGeometry=_geometry;//Create edit operation and executevarop=newArcGIS.Desktop.Editing.EditOperation();op.Name="Create my feature";op.Create(myTemplate,myGeometry);op.Execute();
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=newArcGIS.Desktop.Editing.EditOperation();createOp.Name="Create from insp";createOp.Create(insp.MapMember,insp.ToDictionary(a =>a.FieldName, a =>a.CurrentValue));createOp.Execute();});
Create features from a CSV file
//Run on MCTArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//Create the edit operationvarcreateOperation=newArcGIS.Desktop.Editing.EditOperation();createOperation.Name="Generate points";createOperation.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.MapPointBuilder.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) operationreturncreateOperation.Execute();});
Edit Operation Clip Features
varclipFeatures=newEditOperation();clipFeatures.Name="Clip Features";clipFeatures.Clip(featureLayer,oid,clipPoly,ClipMode.PreserveArea);//Execute to execute the operation//Must be called within QueuedTask.RunclipFeatures.Execute();//or use async flavor//await clipFeatures.ExecuteAsync();
Edit Operation Cut Features
varselect=MapView.Active.SelectFeatures(clipPoly);varcutFeatures=newEditOperation();cutFeatures.Name="Cut Features";cutFeatures.Split(featureLayer,oid,cutLine);//Cut all the selected features in the active view//Select using a polygon (for example)varkvps=MapView.Active.SelectFeatures(polygon).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));cutFeatures.Split(kvps,cutLine);//Execute to execute the operation//Must be called within QueuedTask.RuncutFeatures.Execute();//or use async flavor//await cutFeatures.ExecuteAsync();
Edit Operation Delete Features
vardeleteFeatures=newEditOperation();deleteFeatures.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)varselection=MapView.Active.SelectFeatures(polygon).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));deleteFeatures.Delete(selection);//Execute to execute the operation//Must be called within QueuedTask.RundeleteFeatures.Execute();//or use async flavor//await deleteFeatures.ExecuteAsync();
Edit Operation Duplicate Features
varduplicateFeatures=newEditOperation();duplicateFeatures.Name="Duplicate Features";//Duplicate with an X and Y offset of 500 map unitsduplicateFeatures.Duplicate(featureLayer,oid,500.0,500.0,0.0);//Execute to execute the operation//Must be called within QueuedTask.RunduplicateFeatures.Execute();//or use async flavor//await duplicateFeatures.ExecuteAsync();
Edit Operation Explode Features
varexplodeFeatures=newEditOperation();explodeFeatures.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.RunexplodeFeatures.Execute();//or use async flavor//await explodeFeatures.ExecuteAsync();
Edit Operation Merge Features
varmergeFeatures=newEditOperation();mergeFeatures.Name="Merge Features";//Merge three features into a new feature using defaults//defined in the current templatemergeFeatures.Merge(this.CurrentTemplateasEditingFeatureTemplate,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.RunmergeFeatures.Execute();//or use async flavor//await mergeFeatures.ExecuteAsync();
Edit Operation Modify single feature
varmodifyFeature=newEditOperation();modifyFeature.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.RunmodifyFeature.Execute();//or use async flavor//await modifyFeatures.ExecuteAsync();
Edit Operation Modify multiple features
//Search by attributevarqueryFilter=newQueryFilter();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=newEditOperation();modifyFeatures.Name="Modify features";modifyFeatures.ShowProgressor=true;varmuultipleFeaturesInsp=newInspector();muultipleFeaturesInsp.Load(featureLayer,oidSet);muultipleFeaturesInsp["MOMC"]=24;modifyFeatures.Modify(muultipleFeaturesInsp);modifyFeatures.ExecuteAsync();
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();filter.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=newArcGIS.Desktop.Editing.EditOperation();modifyOp.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);modifyOp.Execute();});
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();moveFeature.Name="Move features";moveFeature.Move(selectionDictionary,10,10);//specify your units along axis to move the geometrymoveFeature.Execute();
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=newMapPointBuilder(1.0,2.0,3.0,4.0,MapView.Active.Map.SpatialReference);//can pass in coordinates.varmodifyFeatureCoord=newEditOperation();modifyFeatureCoord.Name="Move features";modifyFeatureCoord.Modify(abLayer,selOid,moveToPoint.ToGeometry());//Modify the feature to the new geometry modifyFeatureCoord.Execute();
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();planarizeFeatures.Name="Planarize Features";//Planarize one or more featuresplanarizeFeatures.Planarize(featureLayer,newList<long>(){oid});//Execute to execute the operation//Must be called within QueuedTask.RunplanarizeFeatures.Execute();//or use async flavor//await planarizeFeatures.ExecuteAsync();
Edit Operation ParallelOffset
//Create parrallel features from the selected features//find the roads layervarroadsLayer=MapView.Active.Map.FindLayers("Roads").FirstOrDefault();//instatiate 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 executevarparrallelOp=newEditOperation();parrallelOp.Create(parOffsetBuilder);parrallelOp.Execute();
Edit Operation Reshape Features
varreshapeFeatures=newEditOperation();reshapeFeatures.Name="Reshape Features";reshapeFeatures.Reshape(featureLayer,oid,modifyLine);//Reshape a set of features that intersect some geometry....varselFeatures=MapView.Active.GetFeatures(modifyLine).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));reshapeFeatures.Reshape(selFeatures,modifyLine);//Execute to execute the operation//Must be called within QueuedTask.RunreshapeFeatures.Execute();//or use async flavor//await reshapeFeatures.ExecuteAsync();
Edit Operation Rotate Features
varrotateFeatures=newEditOperation();rotateFeatures.Name="Rotate Features";//Rotate works on a selected set of features//Get all features that intersect a polygonvarrotateSelection=MapView.Active.GetFeatures(polygon).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));//Rotate selected features 90 deg about "origin"rotateFeatures.Rotate(rotateSelection,origin,Math.PI/2);//Execute to execute the operation//Must be called within QueuedTask.RunrotateFeatures.Execute();//or use async flavor//await rotateFeatures.ExecuteAsync();
Edit Operation Scale Features
varscaleFeatures=newEditOperation();scaleFeatures.Name="Scale Features";//Rotate works on a selected set of featuresvarscaleSelection=MapView.Active.GetFeatures(polygon).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));//Scale the selected features by 2.0 in the X and Y directionscaleFeatures.Scale(scaleSelection,origin,2.0,2.0,0.0);//Execute to execute the operation//Must be called within QueuedTask.RunscaleFeatures.Execute();//or use async flavor//await scaleFeatures.ExecuteAsync();
Edit Operation Split Features
varsplitFeatures=newEditOperation();splitFeatures.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.RunsplitFeatures.Execute();//or use async flavor//await splitAtPointsFeatures.ExecuteAsync();
Edit Operation Transform Features
vartransformFeatures=newEditOperation();transformFeatures.Name="Transform Features";//Transform a selected set of featuresvartransformSelection=MapView.Active.GetFeatures(polygon).Select(
k =>newKeyValuePair<MapMember,List<long>>(k.KeyasMapMember,k.Value));transformFeatures.Transform(transformSelection,linkLayer);//Transform just a layertransformFeatures.Transform(featureLayer,linkLayer);//Perform an affine transformationtransformFeatures.TransformAffine(featureLayer,linkLayer);//Execute to execute the operation//Must be called within QueuedTask.RuntransformFeatures.Execute();//or use async flavor//await transformFeatures.ExecuteAsync();
Edit Operation Perform a Clip, Cut, and Planarize
// note - EditOperation.Planarize requires a standard license. // An exception will be thrown if Pro is running under a basic license.//Multiple operations can be performed by a single//edit operation.varclipCutPlanarizeFeatures=newEditOperation();clipCutPlanarizeFeatures.Name="Clip, Cut, and Planarize Features";clipCutPlanarizeFeatures.Clip(featureLayer,oid,clipPoly);clipCutPlanarizeFeatures.Split(featureLayer,oid,cutLine);clipCutPlanarizeFeatures.Planarize(featureLayer,newList<long>(){oid});//Note: An edit operation is a single transaction. //Execute the operations (in the order they were declared)clipCutPlanarizeFeatures.Execute();//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 attachement. //Adding an attachment requires the object id (of a new feature) has already been created. vareditOperation1=newEditOperation();editOperation1.Name=string.Format("Create point in '{0}'",CurrentTemplate.Layer.Name);longnewFeatureID=-1;//The Create operation has to execute so we can get an object_ideditOperation1.Create(this.CurrentTemplate,polygon,(object_id)=>newFeatureID=object_id);//Must be within a QueuedTaskeditOperation1.Execute();//or use async flavor//await editOperation1.ExecuteAsync();//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 attachement using the new feature ideditOperation2.AddAttachment(this.CurrentTemplate.Layer,newFeatureID,@"C:\data\images\Hydrant.jpg");//editOperation1 and editOperation2 show up as a single Undo operation on the UI even though//we had two transactions//Must be within a QueuedTaskeditOperation2.Execute();//or use async flavor//await editOperation2.ExecuteAsync();
Edit Operation add attachment via RowToken
//ArcGIS Pro 2.5 extends the EditOperation.AddAttachment method to take a RowToken as a paramter.//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.CreateEx(this.CurrentTemplate,polygon);editOpAttach.AddAttachment(attachRowToken,@"c:\temp\image.jpg");//Must be within a QueuedTaskeditOpAttach.Execute();
SetOnUndone, SetOnRedone, SetOnComitted
// SetOnUndone, SetOnRedone and SetOnComittedManage 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.Keys.First(),selectedFeatures.Values.First());testInspector["Name"]="test";//create and execute the edit operationvarupdateTestField=newEditOperation();updateTestField.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");});updateTestField.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
Stop a delete
publicstaticvoidStopADelete(){// subscribe to the RowDeletedEvent for the appropriate tableTabletable=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();RowDeletedEvent.Subscribe(OnRowDeletedEvent,table);}privatestaticvoidOnRowDeletedEvent(RowChangedEventArgsobj){if(_lastEdit!=obj.Guid){//cancel with dialog// Note - feature edits on Hosted and Standard Feature Services cannot be cancelled.obj.CancelEdit("Delete Event\nAre you sure",true);_lastEdit=obj.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(OnRowChangedEvent,featureLayer.GetTable());});}privatestaticvoidOnRowChangedEvent(RowChangedEventArgsobj){//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=obj.Row.GetOriginalValue(shapeIndex)asGeometry;//New geometry of the modified rowvargeomNew=obj.Row[shapeIndex]asGeometry;//Compare the twoboolshapeChanged=geomOrig.IsEqual(geomNew);}
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 stackprivatevoidHookEvents(){// subscribe to the RowCreatedEventTabletable=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();RowCreatedEvent.Subscribe(MyRowCreatedEvent,table);}privatevoidMyRowCreatedEvent(RowChangedEventArgsobj){// get the edit operationvarparentEditOp=obj.Operation;// set up some attributesvarattribues=newDictionary<string,object>{};attribues.Add("Layer","Parcels");attribues.Add("Description","OID: "+obj.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,attribues);}
Modify a record within Row Events
privatevoidHookChangedEvent(){// subscribe to the RowChangedEventTabletable=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault().GetTable();RowChangedEvent.Subscribe(MyRowChangedEvent,table);}privatevoidMyRowChangedEvent(RowChangedEventArgsobj){//example of modifying a field on a row that has been createdvarparentEditOp=obj.Operation;// avoid recursionif(_lastEdit!=obj.Guid){//update field on changeparentEditOp.Modify(obj.Row,"ZONING","New");_lastEdit=obj.Guid;}}
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.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
// 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.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"];
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.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 - but with no undo/redoawaitinspector.ApplyAsync();
Accessing Blob Fields
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.Keys.First(),selectedFeatures.Values.First());//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 fieldvarop=newEditOperation();op.Name="Blob Inspector";insp["Blobfield"]=msr;op.Modify(insp);op.Execute();//read a blob field and save to a file//assume inspector has been loaded with a featurevarmsw=newMemoryStream();msw=insp["Blobfield"]asMemoryStream;using(FileStreamfile=newFileStream(@"d:\temp\blob.jpg",FileMode.Create,FileAccess.Write)){msw.WriteTo(file);}});
Read and Write blob fields with a row cursor in a callback
QueuedTask.Run(()=>{vareditOp=newEditOperation();editOp.Name="Blob Cursor";varfeatLayer=MapView.Active.Map.FindLayers("Hydrant").First()asFeatureLayer;editOp.Callback((context)=>{using(varrc=featLayer.GetTable().Search(null,false)){while(rc.MoveNext()){//read file into memory streamvarmsr=newMemoryStream();using(FileStreamfile=newFileStream(@"d:\images\Hydrant.jpg",FileMode.Open,FileAccess.Read)){file.CopyTo(msr);}using(varrecord=rc.Current){record["BlobField"]=msr;record.Store();//read the blob field to a filevarmsw=newMemoryStream();msw=record["BlobField"]asMemoryStream;using(FileStreamfile=newFileStream(@"d:\temp\blob.jpg",FileMode.Create,FileAccess.Write)){msw.WriteTo(file);}}}}},featLayer.GetTable());editOp.Execute();});
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;}// ...}
// set only Point and Edge snapping modes, clear everything elseArcGIS.Desktop.Mapping.Snapping.SetSnapModes(SnapMode.Point,SnapMode.Edge);// clear all snap modesArcGIS.Desktop.Mapping.Snapping.SetSnapModes();// 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);vee.Vertex=true;vee.Edge=true;vee.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 offArcGIS.Desktop.Mapping.Snapping.SetSnapModes();// 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);vertexOnly.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);snapOptions.SnapToSketchEnabled=true;snapOptions.XYTolerance=100;snapOptions.ZToleranceEnabled=true;snapOptions.ZTolerance=0.6;ArcGIS.Desktop.Mapping.Snapping.SetOptions(myMap,snapOptions);
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
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");});
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"templateDef.ToolProgID=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.GetExcludedToolDamlIds().ToList());//hide all the tools then allow the line toolcimEditTemplate.SetExcludedToolDamlIds(allTools.ToArray());cimEditTemplate.AllowToolDamlID("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 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);});
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);// Execute the operationreturncreateOperation.Execute();});returnresult;}
awaitQueuedTask.Run(()=>{//annoLayer is ~your~ Annotation layer...// use the inspector methodologyvarinsp=newInspector(true);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);op.Execute();});
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 layervarinsp=newInspector(true);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);op.Execute();}});
Modify Annotation Text Graphic
awaitQueuedTask.Run(()=>{varselection=annoLayer.GetSelection();if(selection.GetCount()==0)return;// use the first selelcted feature varinsp=newInspector(true);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);boolresult=op.Execute();});