Select to view content in your preferred language

ChainedEditing not working

741
5
Jump to solution
07-23-2019 05:37 AM
BrianBulla
Honored 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
by Anonymous User
Not applicable

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
by Anonymous User
Not applicable

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
Honored 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
by Anonymous User
Not applicable

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
by Anonymous User
Not applicable

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.

BrianBulla
Honored 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