Hi,
With a selection of features from the same layer in ArcMap, a user clicks on a tool that autopopulates some fields based on some spatial queries. There is a loop to handle when more than one feature is selected.
Before the loop starts I do this:
ar editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new Facility IDs";
var insp = new ArcGIS.Desktop.Editing.Attributes.Inspector(); //create the Inspector
Then I loop through the code and do what needs to be done. The current selected feature has an attribute that the next selected feature will need to use (ie. to increment it's number by 1), so I do this to save the current features edits:
editOperation.Modify(insp);
If I step through my code, up to this point everything is fine and attributes seem to be getting updated. With the next selected feature I will then loop through the code again, but this time my code that looks at the field that needs to increment by one doesn't 'see' the edit made the first time through, so the counter ends up being the same as the previous edit.
At the end of the looping, I do this:
editOperation.Execute();
If I select features one at a time and run things, all is good. It's only when doing multiple selections. Is editOperation.Modify the right method to call and get things saved to the database?? If I run .Modify and .Execute right after the other, then I get a Null Reference the next time I go through the loop....I guess that means I need to recreate the EditOperation each time through, which I don't want to do.
Any ideas on where I am going wrong??
Solved! Go to Solution.
Yes!!! I finally got it working. Looks like the key is to use the ChainedOperation as follows:
var editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new Facility IDs";
< start the looping>
var featureEdit = new ArcGIS.Desktop.Editing.EditOperation();
featureEdit = editOperation.CreateChainedOperation();
<make your edits>
featureEdit.Modify(insp);
featureEdit.Execute();
<end the loop>
editOperation.Execute();
Glad to finally get this working. Thanks for the tip Drew!!
I believe you want to be .Execute() -ing after each iteration, not at the end. You're modifiying the edit operation, but not the actual attribute table (execute does that). So if you're looking for changes in the attribute table none have yet been made.
And if you want all changes to be on the same undo stack, you can chain the operations i.e. editOperation = editOperation.CreateChainedOperation(); at the beginning of the loop
Also note that using .Execute doesn't actually save the changes, so they can still be undone.
Hi Drew,
I tried you suggestions, but to no avail. I think I need to still do .Modify(insp) first and then the .Execute. I tried moving the .Execute up to right after the .Modify. That works for the first pass, but then on the second pass it craps out at the .Modify. I think running .Execute might dispose of the EditOperation object??
This is what I use in my code:
var createOperation = new ArcGIS.Desktop.Editing.EditOperation { Name = "Chained operation" };
.. start loop..
createOperation = createOperation.CreateChainedOperation();
createOperation.Create( ... ); // or modify, same thing
createOperation.Execute();
.. end loop
Hi Drew. From reading about ChainedOperation it sounds like it may work, but I cannot yet get it to.
I'm converting some code from ArcObjects, to ArcPro and following the same logic, but for some reason it's not working in ArcPro.
I'll keep working at it.
I have posted my code on the main thread if you want to check it out. The formatting on this forum makes it a but hard to read though.
Finally got it working!! Thanks for your help.....yes, the ChainedOperation is what I needed.
To paste code snippets go to the 3 dots Expand > More > Syntax Highlighter, set language and paste
Nice....thanks!! I've reposted it....more readable now. 🙂
OK, here is the whole code segment....in a more readable format. Thanks for the tip Drew!!
All this code works....no errors. Just not getting the expected results. Basically the sequenceNumber should increment by one each time. For the first selected feature, GetNextFacilityID determines the next available number, and then each time the loop runs again, that number should increase by 1.
But it's not....I think because the edit isn't saving??
protected override void OnClick()
{
try
{
QueuedTask.Run(() =>
{
Map map = MapView.Active.Map;
//Get each layer that needs to be part of the intersection as a FeatureLayer
FeatureLayer gridLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.LAND_Grid", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
localMunicipalityLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.LAND_LocalMunicipality", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
FeatureLayer administrativeAreaLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.LAND_AdministrativeArea", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
FeatureLayer communitiesLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.LAND_Communities", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
FeatureLayer depotAreaLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.LAND_DepotArea", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
FeatureLayer pressureZoneLayer = map.GetLayersAsFlattenedList().OfType<FeatureLayer>().Where(l => l.Name.IndexOf("GISWRKS1.WORKS.WAT_PressureZone", StringComparison.CurrentCultureIgnoreCase) >= 0).FirstOrDefault();
//Check to make sure they are all in the map
if (gridLayer == null || localMunicipalityLayer == null || administrativeAreaLayer == null || communitiesLayer == null || depotAreaLayer == null || pressureZoneLayer == null)
{
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("Please add the LAND and Water Pressure Zone layers before proceeding.", "Missing Layers");
return;
}
var editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new Facility IDs";
var insp = new ArcGIS.Desktop.Editing.Attributes.Inspector(); //create the Inspector
//Go through each layer in the map
foreach (Layer layer in map.GetLayersAsFlattenedList().OfType<FeatureLayer>())
{
var currentLayer = map.FindLayers(layer.Name).FirstOrDefault() as BasicFeatureLayer;
//Does current layer have FACILITYID field??
Boolean facID_field = false;
List<FieldDescription> theFields = currentLayer.GetFieldDescriptions();
foreach (var fieldName in theFields)
{
if (fieldName.Name.ToString() == "FACILITYID")
facID_field = true;
}
//if the FACILITYID field exists AND there is a selection, then continue....otherwise, look at next layer
if (currentLayer.SelectionCount > 0 && facID_field == true)
{
var selection = currentLayer.GetSelection();
IReadOnlyList<long> selectedOIDs = selection.GetObjectIDs(); //save the selected OBJECTIDs to a list.
structureType = GetFeatureClassCode(currentLayer);
//Loop through each selected OBJECTID, and pass the DRAWING and FACILITYID fields to the OpenDrawing method.
foreach (var oid in selectedOIDs)
{
MapPoint intersectPoint = null;
insp.Load(currentLayer, oid); //use the current selected OBJECTID as you go through the loop of selected features
if (insp["FACILITYID"].ToString() == "")
{
if (insp.GeometryAttribute.GeometryType == GeometryType.Polyline) //for polyline
{
var pointCollection = ((Multipart)insp.Shape).Points;
intersectPoint = pointCollection[0];
}
else
{
intersectPoint = insp.Shape as MapPoint; //for point
}
insp["GRIDNO"] = GetIntersectingField(intersectPoint, "GRIDNO", gridLayer);
insp["LocalMunicipality"] = GetIntersectingField(intersectPoint, "LocalMunicipality", localMunicipalityLayer);
insp["SettlementArea"] = GetIntersectingField(intersectPoint, "NAME", communitiesLayer);
insp["DEPOTAREA"] = GetIntersectingField(intersectPoint, "DEPOTAREA", depotAreaLayer);
FeatureLayer currFLayer = currentLayer as FeatureLayer;
FeatureClass currFClass = currFLayer.GetFeatureClass();
//This is where the sequence number will get incremented each time (in the GetNextFacilityID)
insp["FACILITYID"] = GetNextFacilityID(currFClass, gridNumber);
insp["SEQUENCENO"] = sequenceNumber;
insp["STRUCTURETYPE"] = structureType;
insp["CONTRACTNO"] = GetIntersectingField(intersectPoint, "CONTRACTNO", administrativeAreaLayer);
if ((currentLayer.Name == "GISWRKS1.WORKS.Eff_Watermain") || (currentLayer.Name == "GISWRKS1.WORKS.Eff_WaterService") || (currentLayer.Name == "GISWRKS1.WORKS.Eff_Fitting") || (currentLayer.Name == "GISWRKS1.WORKS.Eff_ControlValve") || (currentLayer.Name == "GISWRKS1.WORKS.Eff_Hydrant"))
insp["WATERTYPE"] = "NONPOT";
if (currentLayer.Name == "GISWRKS1.WORKS.Eff_Hydrant")
insp["SUBTYPECD"] = 3;
if (currentLayer.Name == "GISWRKS1.WORKS.WAT_ControlValve")
insp["PressureZone"] = GetIntersectingField(intersectPoint, "WPZ", pressureZoneLayer);
//ADD code for VLS_Points
editOperation.Modify(insp); //update the current feature
}
} //go to next selected feature in current layer
} //end of if (currentLayer.SelectionCount > 0 && facID_field == true)
} //Go to the next layer in the map
editOperation.Execute();
}); //end of QueuedTask
}
catch (Exception ex)
{
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(ex.ToString());
}
}
And here is the code for GetNextFacilityID:
public string GetNextFacilityID(FeatureClass fClass, string gridNum)
{
try
{
QueryFilter queryFilter = new QueryFilter();
queryFilter.WhereClause = "GRIDNO ='" + gridNum + "'";
queryFilter.SubFields = "SEQUENCENO";
//create an array to hold all of the SEQUENCENO; give it's first member the value zero
ArrayList idList = new ArrayList();
idList.Add(int.Parse("0"));
RowCursor rCursor = fClass.Search(queryFilter);
Feature feature;
while (rCursor.MoveNext())
{
using (feature = (Feature)rCursor.Current)
{
int sequencePosition = feature.FindField("SEQUENCENO");
idList.Add(Convert.ToInt32(feature[sequencePosition]));
}
}
//sort the list so we can get the highest value of SEQUENCENO
idList.Sort();
//the next # we want is the current highest number + 1
int number = (int)idList[idList.Count - 1] + 1;
sequenceNumber = number;
//convert it to a string so we can add the zero placeholders to the front of it....it needs to be 4 digits.
string finalNum = number.ToString();
switch (finalNum.Length)
{
case 1:
finalNum = "000" + finalNum;
break;
case 2:
finalNum = "00" + finalNum;
break;
case 3:
finalNum = "0" + finalNum;
break;
}
//return the full facility id, with the STRUCTURETYPE, GRIDNO, and SEQUENCENO
return structureType + "-" + gridNumber + "-" + finalNum;
}
catch (Exception ex)
{
ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(ex.ToString());
return null;
}
}
Yes!!! I finally got it working. Looks like the key is to use the ChainedOperation as follows:
var editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new Facility IDs";
< start the looping>
var featureEdit = new ArcGIS.Desktop.Editing.EditOperation();
featureEdit = editOperation.CreateChainedOperation();
<make your edits>
featureEdit.Modify(insp);
featureEdit.Execute();
<end the loop>
editOperation.Execute();
Glad to finally get this working. Thanks for the tip Drew!!