varcreateFeatures=new EditOperation();
createFeatures.Name ="Create Features";//Create a feature with a polygon
createFeatures.Create(featureLayer, polygon);//with a callback
createFeatures.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 MapTool
createFeatures.Create(this.CurrentTemplate, polygon);//Execute to execute the operation//Must be called within QueuedTask.Run
createFeatures.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=new ArcGIS.Desktop.Editing.EditOperation();
op.Name ="Create my feature";
op.Create(myTemplate, myGeometry);
op.Execute();
Create feature from a modified inspector
varinsp=new ArcGIS.Desktop.Editing.Attributes.Inspector();
insp.Load(layer,86);
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{// modify attributes if necessary// insp["Field1"] = newValue;//Create new feature from an existing inspector (copying the feature)varcreateOp=new ArcGIS.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 MCT
ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{//Create the edit operationvarcreateOperation=new ArcGIS.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(var item in csvData){//Create the point geometry ArcGIS.Core.Geometry.MapPoint newMapPoint= 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 creation createOperation.Create(layer, atts);}// execute the edit (feature creation) operationreturn createOperation.Execute();});
Edit Operation Clip Features
varclipFeatures=new EditOperation();
clipFeatures.Name ="Clip Features";
clipFeatures.Clip(featureLayer, oid, clipPoly, ClipMode.PreserveArea);//Execute to execute the operation//Must be called within QueuedTask.Run
clipFeatures.Execute();//or use async flavor//await clipFeatures.ExecuteAsync();
Edit Operation Cut Features
varselect= MapView.Active.SelectFeatures(clipPoly);varcutFeatures=new EditOperation();
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.Key as MapMember, k.Value));
cutFeatures.Split(kvps, cutLine);//Execute to execute the operation//Must be called within QueuedTask.Run
cutFeatures.Execute();//or use async flavor//await cutFeatures.ExecuteAsync();
Edit Operation Delete Features
vardeleteFeatures=new EditOperation();
deleteFeatures.Name ="Delete Features";vartable= MapView.Active.Map.StandaloneTables[0];//Delete a row in a standalone table
deleteFeatures.Delete(table, oid);//Delete all the selected features in the active view//Select using a polygon (for example)varselection= MapView.Active.SelectFeatures(polygon).Select(k =>newKeyValuePair<MapMember,List<long>>(k.Key as MapMember, k.Value));
deleteFeatures.Delete(selection);//Execute to execute the operation//Must be called within QueuedTask.Run
deleteFeatures.Execute();//or use async flavor//await deleteFeatures.ExecuteAsync();
Edit Operation Duplicate Features
varduplicateFeatures=new EditOperation();
duplicateFeatures.Name ="Duplicate Features";//Duplicate with an X and Y offset of 500 map units
duplicateFeatures.Duplicate(featureLayer, oid,500.0,500.0,0.0);//Execute to execute the operation//Must be called within QueuedTask.Run
duplicateFeatures.Execute();//or use async flavor//await duplicateFeatures.ExecuteAsync();
Edit Operation Explode Features
varexplodeFeatures=new EditOperation();
explodeFeatures.Name ="Explode Features";//Take a multipart and convert it into one feature per part//Provide a list of ids to convert multiple
explodeFeatures.Explode(featureLayer,newList<long>(){ oid },true);//Execute to execute the operation//Must be called within QueuedTask.Run
explodeFeatures.Execute();//or use async flavor//await explodeFeatures.ExecuteAsync();
Edit Operation Merge Features
varmergeFeatures=new EditOperation();
mergeFeatures.Name ="Merge Features";//Merge three features into a new feature using defaults//defined in the current template
mergeFeatures.Merge(this.CurrentTemplate as EditingFeatureTemplate, featureLayer,newList<long>(){10,96,12});//Merge three features into a new feature in the destination layer
mergeFeatures.Merge(destinationLayer, featureLayer,newList<long>(){10,96,12});//Use an inspector to set the new attributes of the merged featurevarinspector=new Inspector();
inspector.Load(featureLayer, oid);//base attributes on an existing feature//change attributes for the new feature
inspector["NAME"]="New name";
inspector["DESCRIPTION"]="New description";//Merge features into a new feature in the same layer using the//defaults set in the inspector
mergeFeatures.Merge(featureLayer,newList<long>(){10,96,12}, inspector);//Execute to execute the operation//Must be called within QueuedTask.Run
mergeFeatures.Execute();//or use async flavor//await mergeFeatures.ExecuteAsync();
Edit Operation Modify single feature
varmodifyFeature=new EditOperation();
modifyFeature.Name ="Modify a feature";//use an inspectorvarmodifyInspector=new Inspector();
modifyInspector.Load(featureLayer, oid);//base attributes on an existing feature//change attributes for the new feature
modifyInspector["SHAPE"]=polygon;//Update the geometry
modifyInspector["NAME"]="Updated name";//Update attribute(s)
modifyFeature.Modify(modifyInspector);//update geometry and attributes using overloadvarfeatureAttributes=newDictionary<string,object>();
featureAttributes["NAME"]="Updated name";//Update attribute(s)
modifyFeature.Modify(featureLayer, oid, polygon, featureAttributes);//Execute to execute the operation//Must be called within QueuedTask.Run
modifyFeature.Execute();//or use async flavor//await modifyFeatures.ExecuteAsync();
Edit Operation Modify multiple features
//Search by attributevarqueryFilter=new QueryFilter();
queryFilter.WhereClause ="OBJECTID < 1000000";//Create list of oids to updatevaroidSet=newList<long>();using(varrc= featureLayer.Search(queryFilter)){while(rc.MoveNext()){using(varrecord= rc.Current){
oidSet.Add(record.GetObjectID());}}}//create and execute the edit operationvarmodifyFeatures=new EditOperation();
modifyFeatures.Name ="Modify features";
modifyFeatures.ShowProgressor =true;varmuultipleFeaturesInsp=new Inspector();
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()as BasicFeatureLayer;//Search by attributevarfilter=new ArcGIS.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=new ArcGIS.Desktop.Editing.EditOperation(); modifyOp.Name ="Update date";// load features into inspector and update fieldvardateInsp=new ArcGIS.Desktop.Editing.Attributes.Inspector(); dateInsp.Load(disLayer, oids); dateInsp["InspDate"]="9/21/2013";// modify and execute modifyOp.Modify(insp); 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(firstLayer as MapMember, selectionfromMap.GetObjectIDs().ToList());varmoveFeature=new EditOperation();
moveFeature.Name ="Move features";
moveFeature.Move(selectionDictionary,10,10);//specify your units along axis to move the geometry
moveFeature.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=new MapPointBuilder(1.0,2.0,3.0,4.0, MapView.Active.Map.SpatialReference);//can pass in coordinates.varmodifyFeatureCoord=new EditOperation();
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=new EditOperation();
planarizeFeatures.Name ="Planarize Features";//Planarize one or more features
planarizeFeatures.Planarize(featureLayer,newList<long>(){ oid });//Execute to execute the operation//Must be called within QueuedTask.Run
planarizeFeatures.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=new ParallelOffset.Builder(){Selection= MapView.Active.Map.GetSelection(),Template= roadsLayer.GetTemplate("Freeway"),Distance=200,Side= ParallelOffset.SideType.Both,Corner= ParallelOffset.CornerType.Mitered,Iterations=1,AlignConnected=false,CopyToSeparateFeatures=false,RemoveSelfIntersectingLoops=true};//create editoperation and executevarparrallelOp=new EditOperation();
parrallelOp.Create(parOffsetBuilder);
parrallelOp.Execute();
Edit Operation Reshape Features
varreshapeFeatures=new EditOperation();
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.Key as MapMember, k.Value));
reshapeFeatures.Reshape(selFeatures, modifyLine);//Execute to execute the operation//Must be called within QueuedTask.Run
reshapeFeatures.Execute();//or use async flavor//await reshapeFeatures.ExecuteAsync();
Edit Operation Rotate Features
varrotateFeatures=new EditOperation();
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.Key as MapMember, 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.Run
rotateFeatures.Execute();//or use async flavor//await rotateFeatures.ExecuteAsync();
Edit Operation Scale Features
varscaleFeatures=new EditOperation();
scaleFeatures.Name ="Scale Features";//Rotate works on a selected set of featuresvarscaleSelection= MapView.Active.GetFeatures(polygon).Select(k =>newKeyValuePair<MapMember,List<long>>(k.Key as MapMember, k.Value));//Scale the selected features by 2.0 in the X and Y direction
scaleFeatures.Scale(scaleSelection, origin,2.0,2.0,0.0);//Execute to execute the operation//Must be called within QueuedTask.Run
scaleFeatures.Execute();//or use async flavor//await scaleFeatures.ExecuteAsync();
Edit Operation Split Features
varsplitFeatures=new EditOperation();
splitFeatures.Name ="Split Features";varsplitPoints=newList<MapPoint>(){ mp1, mp2, mp3 };//Split the feature at 3 points
splitFeatures.Split(featureLayer, oid, splitPoints);// split using percentagevarsplitByPercentage=new SplitByPercentage(){Percentage=33,SplitFromStartPoint=true};
splitFeatures.Split(featureLayer, oid, splitByPercentage);// split using equal partsvarsplitByEqualParts=new SplitByEqualParts(){NumParts=3};
splitFeatures.Split(featureLayer, oid, splitByEqualParts);// split using single distancevarsplitByDistance=new SplitByDistance(){Distance=27.3,SplitFromStartPoint=false};
splitFeatures.Split(featureLayer, oid, splitByDistance);// split using varying distancevardistances=newList<double>(){12.5,38.2,89.99};varsplitByVaryingDistance=new SplitByVaryingDistance(){Distances=distances,SplitFromStartPoint=true,ProportionRemainder=true};
splitFeatures.Split(featureLayer, oid, splitByVaryingDistance);//Execute to execute the operation//Must be called within QueuedTask.Run
splitFeatures.Execute();//or use async flavor//await splitAtPointsFeatures.ExecuteAsync();
Edit Operation Transform Features
vartransformFeatures=new EditOperation();
transformFeatures.Name ="Transform Features";//Transform a selected set of featuresvartransformSelection= MapView.Active.GetFeatures(polygon).Select(k =>newKeyValuePair<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);//Execute to execute the operation//Must be called within QueuedTask.Run
transformFeatures.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=new EditOperation();
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=new EditOperation();
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_id
editOperation1.Create(this.CurrentTemplate, polygon,(object_id)=>newFeatureID=object_id);//Must be within a QueuedTask
editOperation1.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 id
editOperation2.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 QueuedTask
editOperation2.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=new EditOperation();
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 QueuedTask
editOpAttach.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=new Inspector();
testInspector.Load(selectedFeatures.Keys.First(), selectedFeatures.Values.First());
testInspector["Name"]="test";//create and execute the edit operationvarupdateTestField=new EditOperation();
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)as Geometry;//New geometry of the modified rowvargeomNew= obj.Row[shapeIndex]as Geometry;//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 change
parentEditOp.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=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the feature with ObjectID 'oid' into the inspectorawait inspector.LoadAsync(firstFeatureLayer, oid);
Load map selection into Inspector
// get the currently selected features in the mapvarselectedFeatures= ArcGIS.Desktop.Mapping.MapView.Active.Map.GetSelection();// get the first layer and its corresponding selected feature OIDsvarfirstSelectionSet= selectedFeatures.First();// create an instance of the inspector classvarinspector=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawait inspector.LoadAsync(firstSelectionSet.Key, firstSelectionSet.Value);
Get selected feature's attribute value
// 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=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDs
inspector.Load(firstSelectionSet.Key, firstSelectionSet.Value);//get the value ofvarpscode= inspector["STATE_NAME"];
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=new ArcGIS.Desktop.Editing.Attributes.Inspector();// load the selected features into the inspector using a list of object IDsawait inspector.LoadAsync(firstSelectionSet.Key, firstSelectionSet.Value);// assign the new attribute value to the field "Description"// if more than one features are loaded, the change applies to all features
inspector["Description"]="The new value.";// apply the changes as an edit operation - but with no undo/redoawait inspector.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=new Inspector(); insp.Load(selectedFeatures.Keys.First(), selectedFeatures.Values.First());//read file into memory streamvarmsr=new MemoryStream();using(FileStreamfile=new FileStream(@"d:\images\Hydrant.jpg", FileMode.Open, FileAccess.Read)){ file.CopyTo(msr);}//put the memory stream in the blob fieldvarop=new EditOperation(); 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=new MemoryStream();msw= insp["Blobfield"]as MemoryStream;using(FileStreamfile=new FileStream(@"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=new EditOperation(); editOp.Name ="Blob Cursor";varfeatLayer= MapView.Active.Map.FindLayers("Hydrant").First()as FeatureLayer; editOp.Callback((context)=>{using(varrc= featLayer.GetTable().Search(null,false)){while(rc.MoveNext()){//read file into memory streamvarmsr=new MemoryStream();using(FileStreamfile=new FileStream(@"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=new MemoryStream();msw= record["BlobField"]as MemoryStream;using(FileStreamfile=new FileStream(@"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(await ActivateSelectAsync(true))_inSelMode=true;}}elseif(!_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processing
k.Handled = IsShiftKey(k);}}protectedoverridevoidOnToolKeyUp(MapViewKeyEventArgsk){if(k.Key == System.Windows.Input.Key.W){if(_inSelMode){_inSelMode=false;
k.Handled =true;//process this one// Toggle back to sketch mode. If UseSelection = true// the sketch will be restored
ActivateSelectAsync(false);}}elseif(_inSelMode){//disable effect of Shift in the base class.//Mark the key event as handled to prevent further processing
k.Handled = IsShiftKey(k);}}
Listen to the sketch modified event
// SketchModified event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe the sketch modified event//ArcGIS.Desktop.Mapping.Events.SketchModifiedEvent.Subscribe(OnSketchModified);privatevoidOnSketchModified(ArcGIS.Desktop.Mapping.Events.SketchModifiedEventArgs args){// if not an undo operationif(!args.IsUndo){// what was the sketch before the change?varprevSketch= args.PreviousSketch;// what is the current sketch?varcurrentSketch= args.CurrentSketch;if(currentSketch is Polyline polyline){// Examine the current (last) vertex in the line sketchvarlastSketchPoint= polyline.Points.Last();// do something with the last point}}}
Listen to the before sketch completed event and modify the sketch
// BeforeSketchCompleted event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe to the before sketch completed event//ArcGIS.Desktop.Mapping.Events.BeforeSketchCompletedEvent.Subscribe(OnBeforeSketchCompleted);private Task OnBeforeSketchCompleted(BeforeSketchCompletedEventArgsargs){//assign sketch Z values from default surface and set the sketch geometryvarmodifiedSketch= args.MapView.Map.GetZsFromSurfaceAsync(args.Sketch).Result;
args.SetSketchGeometry(modifiedSketch.Geometry);return Task.CompletedTask;}
Listen to the sketch completed event
// SketchCompleted event is fired by // - COTS construction tools (except annotation, dimension geometry types), // - Edit Vertices, Reshape, Align Features// - 3rd party tools with FireSketchEvents = true//Subscribe to the sketch completed event//ArcGIS.Desktop.Mapping.Events.SketchCompletedEvent.Subscribe(OnSketchCompleted);privatevoidOnSketchCompleted(SketchCompletedEventArgsargs){// get the sketchvarfinalSketch= args.Sketch;// do something with the sketch - audit trail perhaps}
Custom construction tool that fires sketch events
internalclassConstructionTool1:MapTool{publicConstructionTool1(){IsSketchTool=true;UseSnapping=true;// Select the type of construction tool you wish to implement. // Make sure that the tool is correctly registered with the correct component category type in the daml SketchType= SketchGeometryType.Line;//Gets or sets whether the sketch is for creating a feature and should use the CurrentTemplate.UsesCurrentTemplate=true;// set FireSketchEvents property to trueFireSketchEvents=true;}// ...}
// set only Point and Edge snapping modes, clear everything else
ArcGIS.Desktop.Mapping.Snapping.SetSnapModes(SnapMode.Point, SnapMode.Edge);// clear all snap modes
ArcGIS.Desktop.Mapping.Snapping.SetSnapModes();// set snap modes one at a time
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Edge,true);
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.End,true);
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Intersection,true);// get current snap modesvarsnapModes= ArcGIS.Desktop.Mapping.Snapping.SnapModes;// get state of a specific snap modeboolisOn= ArcGIS.Desktop.Mapping.Snapping.GetSnapMode(SnapMode.Vertex);
Configure Snapping (Layer Snappability)
// is the layer snappable?boolisSnappable= fLayer.IsSnappable;// set snappability for a specific layer - needs to run on the MCTawait QueuedTask.Run(()=>{// use an extension method fLayer.SetSnappable(true);// or use the CIM directly//var layerDef = fLayer.GetDefinition() as ArcGIS.Core.CIM.CIMGeoFeatureLayerBase;//layerDef.Snappable = true;//fLayer.SetDefinition(layerDef);});// turn all layers snappability offlayerList= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();await QueuedTask.Run(()=>{foreach(var layer in layerList){ layer.SetSnappable(false);}});
Configure Snapping (LayerSnapModes)
layerList= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>();// configure by layerforeach(var layer in layerList){// find the state of the snapModes for the layervarlsm= ArcGIS.Desktop.Mapping.Snapping.GetLayerSnapModes(layer);boolvertexOn= lsm.Vertex;// or use vertexOn= lsm.GetSnapMode(SnapMode.Vertex);booledgeOn= lsm.Edge;// or use edgeOn= lsm.GetSnapMode(SnapMode.Edge);boolendOn= lsm.End;// or use endOn= lsm.GetSnapMode(SnapMode.End);// update a few snapModes // turn Vertex off
lsm.SetSnapMode(SnapMode.Vertex,false);// intersections on
lsm.SetSnapMode(SnapMode.Intersection,true);// and set back to the layer
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer, lsm);// assign a single snap mode at once
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer, SnapMode.Vertex,false);// turn ALL snapModes on
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,true);// turn ALL snapModes off
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layer,false);}// configure for a set of layers// set Vertex, edge, end on for a set of layers, other snapModes falsevarvee=new LayerSnapModes(false);
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(var layer in dictLSM.Keys){varlsm= dictLSM[layer];
lsm.Intersection =true;}
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(dictLSM);// set all snapModes off for a list of layers
ArcGIS.Desktop.Mapping.Snapping.SetLayerSnapModes(layerList,false);
Configure Snapping (Combined Example)
// interested in only snapping to the vertices of a specific layer of interest and not the vertices of other layers// all other snapModes should be off.// snapping must be on
ArcGIS.Desktop.Mapping.Snapping.IsEnabled =true;// turn all application snapModes off
ArcGIS.Desktop.Mapping.Snapping.SetSnapModes();// set application snapMode vertex on
ArcGIS.Desktop.Mapping.Snapping.SetSnapMode(SnapMode.Vertex,true);// ensure layer snapping is onawait QueuedTask.Run(()=>{ fLayer.SetSnappable(true);});// set vertex snapping onlyvarvertexOnly=new LayerSnapModes(false);
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
//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
public Task ChangeTemplateDefaultToolAsync(ArcGIS.Desktop.Mapping.FeatureLayer flayer,stringtoolContentGUID,stringtemplateName){return ArcGIS.Desktop.Framework.Threading.Tasks.QueuedTask.Run(()=>{// retrieve the edit template form the layer by namevartemplate= flayer?.GetTemplate(templateName)as ArcGIS.Desktop.Editing.Templates.EditingTemplate;// get the definition of the layervarlayerDef= flayer?.GetDefinition()as ArcGIS.Core.CIM.CIMFeatureLayer;if((template==null)||(layerDef==null))return;if(template.DefaultToolID !=this.ID){boolupdateLayerDef=false;if(layerDef.AutoGenerateFeatureTemplates){ layerDef.AutoGenerateFeatureTemplates =false;updateLayerDef=true;}// retrieve the CIM edit template definitionvartemplateDef= template.GetDefinition();// assign the GUID from the tool DAML definition, for example// <tool id="TestConstructionTool_SampleSDKTool" categoryRefID="esri_editing_construction_polyline" ….>// <tooltip heading="">Tooltip text<disabledText /></tooltip>// <content guid="e58239b3-9c69-49e5-ad4d-bb2ba29ff3ea" />// </tool>// then the toolContentGUID would be "e58239b3-9c69-49e5-ad4d-bb2ba29ff3ea" 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(var et in editTemplates){//initialize template by activating default tool et.ActivateDefaultToolAsync();varcimEditTemplate= et.GetDefinition();//get the visible tools on this templatevarallTools= et.ToolIDs.ToList();//add the hidden tools on this template allTools.AddRange(cimEditTemplate.GetExcludedToolDamlIds().ToList());//hide all the tools then allow the line tool cimEditTemplate.SetExcludedToolDamlIds(allTools.ToArray()); cimEditTemplate.AllowToolDamlID("esri_editing_SketchLineTool"); newCIMEditingTemplates.Add(cimEditTemplate);}//update the layer templatesvarlayerDef= featLayer.GetDefinition()as CIMFeatureLayer;// Set AutoGenerateFeatureTemplates to false for template changes to stick layerDef.AutoGenerateFeatureTemplates =false; layerDef.FeatureTemplates = newCIMEditingTemplates.ToArray(); featLayer.SetDefinition(layerDef);});
Create New Template using layer.CreateTemplate
varlayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureLayer>().FirstOrDefault();if(layer==null)return;
QueuedTask.Run(()=>{varinsp=new Inspector(); insp.LoadSchema(layer); insp["Field1"]=value1; insp["Field2"]=value2; insp["Field3"]=value3;vartags=new[]{"Polygon","tag1","tag2"};// set defaultTool using a daml-id stringdefaultTool="esri_editing_SketchCirclePolygonTool";// tool filter is the tools to filter OUTvartoolFilter=new[]{"esri_editing_SketchTracePolygonTool"};// create a new template varnewTemplate= layer.CreateTemplate("My new template","description", insp, defaultTool, tags, toolFilter);});
Create 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()as ArcGIS.Core.Data.Mapping.AnnotationFeatureClass;// get the featureclass CIM definition which contains the labels, symbolsvarcimDefinition= fc.GetDefinition()as ArcGIS.Core.Data.Mapping.AnnotationFeatureClassDefinition;varlabels= cimDefinition.GetLabelClassCollection();varsymbols= cimDefinition.GetSymbolCollection();// make sure there are labels, symbolsif((labels.Count ==0)||(symbols.Count ==0))return;// find the label class required// typically you would use a subtype name or some other characteristic// in this case lets just use the first onevarlabel= labels[0];// each label has a textSymbol// the symbolName *should* be the symbolID to be usedvarsymbolName= label.TextSymbol.SymbolName;intsymbolID=-1;if(!int.TryParse(symbolName,out symbolID)){// int.TryParse fails - attempt to find the symbolName in the symbol collectionforeach(var symbol in symbols){if(symbol.Name ==symbolName){symbolID= symbol.ID;break;}}}// no symbol?if(symbolID==-1)return;// load the schemainsp=new Inspector(); insp.LoadSchema(annoLayer);// ok to assign these fields using the inspector[fieldName] methodology// these fields are guaranteed to exist in the annotation schema insp["AnnotationClassID"]= label.ID; insp["SymbolID"]=symbolID;// set up some additional annotation propertiesAnnotationPropertiesannoProperties= insp.GetAnnotationProperties(); annoProperties.FontSize =36; annoProperties.TextString ="My Annotation feature"; annoProperties.VerticalAlignment = VerticalAlignment.Top; annoProperties.HorizontalAlignment = HorizontalAlignment.Justify; insp.SetAnnotationProperties(annoProperties);vartags=new[]{"Annotation","tag1","tag2"};// use daml-id rather than guidstringdefaultTool="esri_editing_SketchStraightAnnoTool";// tool filter is the tools to filter OUTvartoolFilter=new[]{"esri_editing_SketchCurvedAnnoTool"};// create a new template varnewTemplate= annoLayer.CreateTemplate("new anno template","description", insp, defaultTool, tags, toolFilter);});
Annotation
Annotation Construction Tool
//In your config.daml...set the categoryRefID//<tool id="..." categoryRefID="esri_editing_construction_annotation" caption="Create Anno" ...>//Sketch type Point or Line or BezierLine in the constructor...//internal class AnnoConstructionTool : MapTool {// public AnnoConstructionTool() {// IsSketchTool = true;// UseSnapping = true;// SketchType = SketchGeometryType.Point;//protectedasyncoverrideTask<bool>OnSketchCompleteAsync(Geometrygeometry){if(CurrentTemplate==null||geometry==null)returnfalse;// Create an edit operationvarcreateOperation=new EditOperation();
createOperation.Name =string.Format("Create {0}", CurrentTemplate.Layer.Name);
createOperation.SelectNewFeatures =true;varinsp= CurrentTemplate.Inspector;varresult=await QueuedTask.Run(()=>{// get the annotation properties classAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// set custom annotation properties annoProperties.TextString ="my custom text"; annoProperties.Color = ColorFactory.Instance.RedRGB; annoProperties.FontSize =24; annoProperties.FontName ="Arial"; annoProperties.HorizontalAlignment = ArcGIS.Core.CIM.HorizontalAlignment.Right; annoProperties.Shape =geometry;// assign annotation properties back to the inspector insp.SetAnnotationProperties(annoProperties);// Queue feature creation createOperation.Create(CurrentTemplate.Layer, insp);// Execute the operationreturn createOperation.Execute();});returnresult;}
await QueuedTask.Run(()=>{//annoLayer is ~your~ Annotation layer...// use the inspector methodologyvarinsp=new Inspector(true); insp.Load(annoLayer, oid);// get the annotation propertiesAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// set the attribute annoProperties.TextString ="Hello World";// assign the annotation proeprties back to the inspector insp.SetAnnotationProperties(annoProperties);//create and execute the edit operationEditOperationop=new EditOperation(); op.Name ="Update annotation"; op.Modify(insp); op.Execute();});
Modify Annotation Shape
await QueuedTask.Run(()=>{//Don't use 'Shape'....Shape is the bounding box of the annotation text. This is NOT what you want...////var insp = new Inspector();//insp.Load(annoLayer, oid);//var shape = insp["SHAPE"] as Polygon;//...wrong shape...//Instead, we must use the AnnotationProperties//annoLayer is ~your~ Annotation layervarinsp=new Inspector(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=new EditOperation(); op.Name ="Change annotation angle"; op.Modify(insp); op.Execute();}});
Modify Annotation Text Graphic
await QueuedTask.Run(()=>{varselection= annoLayer.GetSelection();if(selection.GetCount()==0)return;// use the first selelcted feature varinsp=new Inspector(true); insp.Load(annoLayer, selection.GetObjectIDs().FirstOrDefault());// getAnnoProperties should return null if not an annotation featureAnnotationPropertiesannoProperties= insp.GetAnnotationProperties();// get the textGraphicCIMTextGraphictextGraphic= annoProperties.TextGraphic;// change text textGraphic.Text ="Hello world";// set x,y offset via the symbolvarsymbol= textGraphic.Symbol.Symbol;vartextSymbol= symbol as CIMTextSymbol; textSymbol.OffsetX =2; textSymbol.OffsetY =3; textSymbol.HorizontalAlignment = HorizontalAlignment.Center;// load the updated textGraphic annoProperties.LoadFromTextGraphic(textGraphic);// assign the annotation properties back insp.SetAnnotationProperties(annoProperties);EditOperationop=new EditOperation(); op.Name ="modify symbol"; op.Modify(insp);boolresult= op.Execute();});