Search code examples
c#.netautocad

Passing ObjectIds to method seems to break the process - BURST example in C#


I have a method that mimics the BURST command but using the .NET interface with AutoCAD. Select a block, type the command, works no problem. But what I'm trying to do is make it into a callable method so that I can get a collection of ObjectIds, all BlockReferences, and have it BURST them one at a time.

I have the BURST method working, and can prove that the correct ObjectIds are making it to the method call each time. It loops through each entity in the collected blocks after the .Explode command, but for some reason the work never gets published (re: nothing ends up exploded).

I'm stuck, been working on this for hours now and it's clearly beyond my understanding. I'm guessing it's one tiny thing I overlooked but can't see it.

// BURST command that bursts ALL blocks
[CommandMethod("testBurstAll")]
public void cmdTestBurstAll()
{
    // Definitions
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor ed = doc.Editor;
    Database db = doc.Database;

    List<ObjectId> passedObjectId = new List<ObjectId>();

    try
    {
        using (Transaction tr = db.TransactionManager.StartTransaction())
        {
            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
            ObjectId oID = bt[BlockTableRecord.ModelSpace];
            BlockTableRecord btr = tr.GetObject(oID, OpenMode.ForWrite) as BlockTableRecord;
            //ed.WriteMessage("Made it to end of BT and BTR definitions");

            RXClass rxBlockRef = RXClass.GetClass(typeof(BlockReference));
            //ed.WriteMessage("Made it to RXClass definition");
            foreach (ObjectId id in btr)
            {
                if (id.IsNull || id.IsErased || id.IsEffectivelyErased || !id.IsValid)
                    continue;

                // get a list of ObjectIds for future method call
                if (id.ObjectClass == rxBlockRef)
                {
                    passedObjectId.Add(id);
                    ed.WriteMessage("\nOriginal method ObjectId: " + id.ToString());
                    /*using (BlockReference reference = tr.GetObject(id, OpenMode.ForWrite) as BlockReference)
                    {
                        ed.WriteMessage("Made it into explode");
                        
                        //reference.ExplodeToOwnerSpace();
                        reference.Erase();
                        //ed.WriteMessage("Made it past explode");
                    }*/
                }
            }

            // Call BURST method for each BlockReference ObjectId added to list
            for( int i = 0; i < passedObjectId.Count; i++)
            {
                cmdTestBurstMethod(passedObjectId.ElementAt(i));
            }
        }
    }
    catch 
    {
        ed.WriteMessage("CATCH used, exception caught");
    }
}

// BURST method - to be called by other commands - needs an ObjectID as input
public void cmdTestBurstMethod(ObjectId passedId)
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    //ed.WriteMessage("\nMade it into BURST method call with: " + passedId.ToString());

    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
        BlockTableRecord ms = tr.GetObject(msId, OpenMode.ForWrite) as BlockTableRecord;

        BlockReference blockRef = tr.GetObject(passedId, OpenMode.ForRead) as BlockReference;

        ed.WriteMessage("\nInto BURST method with OjbectId: " + blockRef.ObjectId.ToString());
        if (blockRef != null)
        {
            DBObjectCollection toAddColl = new DBObjectCollection();
            BlockTableRecord blockDef = tr.GetObject(blockRef.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;

            // Create a text for const and visible attributes 
            foreach (ObjectId entId in blockDef)
            {
                if (entId.ObjectClass.Name == "AcDbAttributeDefinition")
                {
                    AttributeDefinition attDef = tr.GetObject(entId, OpenMode.ForRead) as AttributeDefinition;

                    if ((attDef.Constant && !attDef.Invisible))
                    {
                        DBText text = new DBText();
                        text.Height = attDef.Height;
                        text.TextString = attDef.TextString;
                        text.Position = attDef.Position.TransformBy(blockRef.BlockTransform);
                        toAddColl.Add(text);
                    }
                }
            }

            // Create a text for non-const and visible attributes 
            foreach (ObjectId attRefId in blockRef.AttributeCollection)
            {
                AttributeReference attRef = tr.GetObject(attRefId, OpenMode.ForRead) as AttributeReference;

                if (attRef.Invisible == false)
                {
                    DBText text = new DBText();
                    text.Height = attRef.Height;

                    text.TextString = attRef.TextString;
                    text.Position = attRef.Position;
                    toAddColl.Add(text);
                }
            }
            
            // Get the entities from the block reference Attribute definitions have been taken care of.. so ignore them 
            DBObjectCollection entityColl = new DBObjectCollection();
            blockRef.Explode(entityColl);

            // Get Layer of existing Block and move any objects in that block to the existing layer of the Block (anything on layer '0')
            ed.WriteMessage("\nblockRef NOT NULL");
            String exLayer = blockRef.Layer;
            foreach (Entity ent in entityColl)
            {
                ed.WriteMessage("\nForEach loop entered with: " + ent.ObjectId.ToString());
                if (!(ent is AttributeDefinition))
                {
                    ed.WriteMessage("\nEntered IF !AttributeDefinition");
                    if (ent.Layer == "0")
                    {
                        ed.WriteMessage("\nEntered IF on Layer zero");
                        ent.Layer = exLayer;
                    }
                    toAddColl.Add(ent);
                }
            }
            // Add the entities to modelspace 
            foreach (Entity ent in toAddColl)
            {
                ms.AppendEntity(ent);
                tr.AddNewlyCreatedDBObject(ent, true);
            }
            // Erase the block reference 
            blockRef.UpgradeOpen();
            blockRef.Erase();
        }
        tr.Commit();
    }
}

Solution

  • Nothing is exploded because the parent transaction was not committed.
    So every child transaction is rolled back.
    One way to avoid starting a new child transaction for each ObjectId is to pass the parent transaction as an argument.
    Then commit the parent transaction in the top level method, after bursting.

    Here's a quick and dirty example:

        [CommandMethod("TEST")]
        public static void Test()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var modelSpace = (BlockTableRecord)tr.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
                var brClass = RXObject.GetClass(typeof(BlockReference));
                var ids = modelSpace
                    .Cast<ObjectId>()
                    .Where(id => id.ObjectClass == brClass);
                foreach (ObjectId id in ids)
                {
                    Burst(id, modelSpace, tr);
                }
                tr.Commit();
            }
        }
    
        private static void Burst(ObjectId id, BlockTableRecord owner, Transaction tr)
        {
            var br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
            foreach (ObjectId attId in br.AttributeCollection)
            {
                var attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
                CreateTextFromAttrib(attRef, br.Layer, owner, tr);
                attRef.Erase();
            }
            var entities = new DBObjectCollection();
            br.Explode(entities);
            foreach (Entity entity in entities)
            {
                if (entity is AttributeDefinition)
                {
                    var attDef = (AttributeDefinition)entity;
                    if (attDef.Constant)
                    {
                        CreateTextFromAttrib(attDef, br.Layer, owner, tr);
                    }
                    entity.Dispose();
                }
                else
                {
                    if (entity.Layer == "0") entity.Layer = br.Layer;
                    if (entity.ColorIndex == 0) entity.ColorIndex = 256;
                    owner.AppendEntity(entity);
                    tr.AddNewlyCreatedDBObject(entity, true);
                }
            }
            br.Erase();
        }
    
        private static void CreateTextFromAttrib(DBText att, string layer, BlockTableRecord owner, Transaction tr)
        {
            var text = new DBText
            {
                Rotation = att.Rotation,
                TextString = att.TextString,
                Height = att.Height,
                Justify = att.Justify,
                Position = att.Position
            };
            text.SetPropertiesFrom(att);
            if (att.Justify != AttachmentPoint.BaseLeft)
            {
                text.AlignmentPoint = att.AlignmentPoint;
            }
            if (text.Layer == "0") text.Layer = layer;
            if (text.ColorIndex == 0) text.ColorIndex = 256;
            owner.AppendEntity(text);
            tr.AddNewlyCreatedDBObject(text, true);
        }