ChainedEditing not working

178
5
Jump to solution
07-23-2019 05:37 AM
BrianBulla
Regular Contributor

Hi,

I'm trying to figure out where I am going wrong in my edit sequence.  What I am doing is selecting newly created features that have no attribute information entered.  The user then runs the tool and an attribute get populated.  The field is a numeric field and there is some code that looks for the last entered number (ie. the current highest number) and increases it by 1 for the new feature.

So, for example the user adds 3 new hydrants and selects them all, then runs the tool.  If there are already 10 hydrants the 3 new hydrants should get the numbers 11, 12, and 13 added into the SEQUENCENO attribute for the new features.

My problem is that the 11 and 12 get added, but then third feature gets 12 added again.  If I have 10 features selected, the same sort of pattern happens.  The 11 and 12 get added, but then the remaining 8 would also get 12. 

I only want one item to show on the Undo stack, and each edit will affect the next, so that is why I am using ChainedOperation, but I just don't think I am implementing it correctly.

Below is the code where it loops though all the selected features and assigns the next number.

If you can offer some advice on how to get this working I'd appreciate it.  

                    //Create the MAIN edit operation...this will show up on the Undo Stack
var editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new IDs";
editOperation.EditOperationType = ArcGIS.Desktop.Editing.EditOperationType.Long;

ArcGIS.Desktop.Editing.EditOperation chainedOp = null;

//create the Inspector Object
var inspector = new ArcGIS.Desktop.Editing.Attributes.Inspector();

//create a flag to track the 'main' operation
bool isMainOperation = true;

//Loop through each selected OBJECTID.
foreach (var oid in selectedOIDs)
{
inspector.Clear();
inspector.Load(currentLayer, oid);

//Need to pass a FeatureCLass to the GetNextFaciltyID procedure
FeatureLayer currFLayer = currentLayer as FeatureLayer;
FeatureClass currFClass = currFLayer.GetFeatureClass();

//This is where the sequence number will get incremented each time (in the GetNextFacilityID)
inspector["FACILITYID"] = GetNextFacilityID(currFClass, gridNumber);
inspector["SEQUENCENO"] = sequenceNumber;
}

if (isMainOperation)
{
editOperation.Modify(inspector);
bool result = editOperation.Execute();

//change the flag so we use the ChainedOp next
isMainOperation = false;
}
else
{
//only create the ChainedOp once
if (chainedOp == null)
chainedOp = editOperation.CreateChainedOperation();

//queue up a Modify
chainedOp.Modify(inspector);
}

//Increase the feature count for the final tally
featureCount++;
}
else
{
rejectedCount++;
}

} //go to next selected feature in current layer‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

if ((chainedOp != null) && !chainedOp.IsEmpty)
{
bool chainedResult = chainedOp.Execute();
}

And this is the section of code that determines the next available number.  I think the logic here is fine, but because my edits aren't really getting applied (??) properly though the chainedOp, its not getting the right next number.

        //This will return the next FacilityID
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;
}
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
SeanJones
Esri Regular Contributor

oh and as was just pointed out to me, you need to create the chained op each time in the loop. Take out the null check.

I missed that.

View solution in original post

5 Replies
SeanJones
Esri Regular Contributor

Hey Brian,

The chainedOp.Execute() method needs to be in the loop, after chainedOp.Modify. This will update the table with the incremented value to be read in the next loop, otherwise you just get a bunch of records with the same value.

Does rejectedCount++ ever get hit? I cant see where the else block lines up.

0 Kudos
BrianBulla
Regular Contributor

Hi Sean Jones‌,

Yes, I have tried adding a chainedOp.Execute right after the Modify, but then the code crashes.  Without it, I just get the same value plugged into each feature as you mentioned.

No, rejectedCount is really just some random in development code that never gets hit.  

I've been messing with this all day, breaking it down into a more simplified version, but I get the same results.  I'm really baffled as to how to get this working.

Here is everything I have if it helps.  Should be simpler and easier to read.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ArcGIS.Core.CIM;
using ArcGIS.Core.Data;
using ArcGIS.Core.Geometry;
using ArcGIS.Desktop.Catalog;
using ArcGIS.Desktop.Core;
using ArcGIS.Desktop.Editing;
using ArcGIS.Desktop.Extensions;
using ArcGIS.Desktop.Framework;
using ArcGIS.Desktop.Framework.Contracts;
using ArcGIS.Desktop.Framework.Dialogs;
using ArcGIS.Desktop.Framework.Threading.Tasks;
using ArcGIS.Desktop.Mapping;
using System.Collections;

namespace ArcPro_DSM_Settings
{
internal class TEST : Button
{
string gridNumber, structureType;
int sequenceNumber;

protected override void OnClick()
{
QueuedTask.Run(() =>
{
Map map = MapView.Active.Map;

//Create the MAIN edit operation...this will show up on the Undo Stack
var editOperation = new ArcGIS.Desktop.Editing.EditOperation();
editOperation.Name = "Create new Facility IDs";
editOperation.ProgressMessage = "Calculating FacilityID's";
editOperation.ShowProgressor = true;
editOperation.CancelMessage = "Operation Cancelled";
editOperation.ErrorMessage = "Error Updating Attributes";
editOperation.EditOperationType = ArcGIS.Desktop.Editing.EditOperationType.Long;


//create a flag to track the 'main' operation
bool isMainOperation = true;

ArcGIS.Desktop.Editing.EditOperation chainedOp = null;

//create the Inspector Object
var inspector = new ArcGIS.Desktop.Editing.Attributes.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;

var selection = currentLayer.GetSelection();
IReadOnlyList<long> selectedOIDs = selection.GetObjectIDs();

int nextNum = 0;

//Loop through each selected OBJECTID, and pass the DRAWING and FACILITYID fields to the OpenDrawing method.
foreach (var oid in selectedOIDs)
{
inspector.Clear();
inspector.Load(currentLayer, oid);

//Need to pass a FeatureCLass to the GetNextFaciltyID procedure
FeatureLayer currFLayer = currentLayer as FeatureLayer;
FeatureClass currFClass = currFLayer.GetFeatureClass();

//This is where the sequence number will get incremented each time (in the GetNextFacilityID)
inspector["FACILITYID"] = GetNextFacilityID(currFClass, "Y27");
inspector["SEQUENCENO"] = sequenceNumber;

//editOperation.Modify(inspector);

if (isMainOperation)
{
editOperation.Modify(inspector);
bool result = editOperation.Execute();

//change the flag so we use the ChainedOp next
isMainOperation = false;
}
else
{
//only create the ChainedOp once
if (chainedOp == null)
chainedOp = editOperation.CreateChainedOperation();

//queue up a Modify
chainedOp.Modify(inspector);
bool chainResult = chainedOp.Execute();
}
}

}

//if (!editOperation.IsEmpty)
// editOperation.Execute();

if ((chainedOp != null) && !chainedOp.IsEmpty)
{
bool chainedResult = chainedOp.Execute();
}

});

}


//This will return the next FacilityID
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;
}
}




}
}
0 Kudos
SeanJones
Esri Regular Contributor

The error message isnt that helpful. It could be complaining about the inspector object. Instead of Inspector.Clear in the loop try creating a new one there and use that before the load.

0 Kudos
SeanJones
Esri Regular Contributor

oh and as was just pointed out to me, you need to create the chained op each time in the loop. Take out the null check.

I missed that.

View solution in original post

BrianBulla
Regular Contributor

Hi Sean Jones‌,

Great!  Thanks for the help.  The Inspector.Clear didn't really make any difference, but creating the chainedOp each time through the loop is what made the difference.

I'd previously used a ChainedOp when I was trying to add a group of edits as one item on the Undo stack.  Doing what is apparantly the 'wrong way' worked in that case because I was not dependent on the results of previous edits for the next edit.  But when you do need that previous edit for the next, it looks like creating the chainedOp each time is essential.

Thanks again.

                                    if (isMainOperation)
{
editOperation.Modify(inspector);
bool result = editOperation.Execute();

//change the flag so we use the ChainedOp next
isMainOperation = false;
}
else
{
chainedOp = editOperation.CreateChainedOperation();

//queue up a Modify
chainedOp.Modify(inspector);
chainedOp.Execute();
}
0 Kudos