effective block of anonymous block

effective block of anonymous block

Amremad
Collaborator Collaborator
2,485 Views
9 Replies
Message 1 of 10

effective block of anonymous block

Amremad
Collaborator
Collaborator

Hi All 

 

this function 

        public static string GetEffectiveName(BlockReference blkref)
        {
            string name;
            using (Transaction acTrans = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = (BlockTableRecord)acTrans.GetObject(blkref.DynamicBlockTableRecord, OpenMode.ForRead);
                name = btr.Name;
            }
            return name;
        }

return the name of effective name of anonymous block 

 

iam trying to write this code 

 

        public static BlockReference GetEffectiveBlock(BlockReference blkref)
        {
            BlockReference blk;
            using (Transaction acTrans = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument.TransactionManager.StartTransaction())
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;

                BlockTable bt = (BlockTable)acTrans.GetObject(db.BlockTableId, OpenMode.ForRead);
                ObjectId idBTR = bt[GetEffectiveName(blkref)];

                BlockTableRecord btr = acTrans.GetObject(idBTR, OpenMode.ForRead) as BlockTableRecord;
                using (BlockReference brf = new BlockReference(new Point3d(0, 0, 0), idBTR))
                {
                    blk = brf;
                }
           }
            return blk;
        }

to return the refrence block of this block but it's doesn't work 

 

any help ?

0 Likes
2,486 Views
9 Replies
Replies (9)
Message 2 of 10

_gile
Consultant
Consultant

Hi,

 

You should not use any DBObject outside of the scope of the transaction used to open it or create it, your GetEffectiveBlock function should return the block reference ObjectId instead.

 

That said, you neither add the newly created BlockReference to any BlockTableRecord nor to the transaction and you do not commit the transaction.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 10

_gile
Consultant
Consultant

Here's an example which returns a BlockReference, it have to be called from inside the scope of the transaction used to open the BlockReference instance passed as argument.

 

        public BlockReference InsertEffectiveBlock(BlockReference br)
        {
            if (br == null)
                throw new ArgumentNullException("br");

            var tr = br.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);

            // get the objectId of the 'effective block table record'
            var btrId = br.IsDynamicBlock ? br.DynamicBlockTableRecord : br.BlockTableRecord;

            // get the owner (BlockTableRecord) of the block reference
            var owner = (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForWrite);

            // create a new reference to the 'eefective' block definition
            var newBr = new BlockReference(Point3d.Origin, btrId);

            // insert the newly created reference an get its ObjectId
            owner.AppendEntity(newBr);
            tr.AddNewlyCreatedDBObject(newBr, true);

            // return the block reference
            return newBr;
        }

Another way, which can be safely used independantly to any transaction is to use an ObjectId as argument, use a separated transaction and return an ObjectId.

 

        public ObjectId InsertEffectiveBlock(ObjectId brId)
        {
            if (brId.IsNull)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NullObjectId);
            
            var db = brId.Database;
            if (db == null)
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotInDatabase);

            if (brId.ObjectClass != RXObject.GetClass(typeof(BlockReference)))
                throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.WrongObjectType);

            ObjectId newBrId;
            // start a transaction
            using (var tr = db.TransactionManager.StartTransaction())
            {
                // open the block reference
                var br = (BlockReference)tr.GetObject(brId, OpenMode.ForRead);
                
                // get the objectId of the 'effective block table record'
                var btrId = br.IsDynamicBlock ? br.DynamicBlockTableRecord : br.BlockTableRecord;

                // get the owner (BlockTableRecord) of the block reference
                var owner = (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForWrite);

                // create a new reference to the 'eefective' block definition
                var newBr = new BlockReference(Point3d.Origin, btrId);

                // insert the newly created reference an get its ObjectId
                newBrId = owner.AppendEntity(newBr);
                tr.AddNewlyCreatedDBObject(newBr, true);

                // save changes
                tr.Commit();
            }
            // return the ObjectId of the newly created block reference
            return newBrId;
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 4 of 10

ActivistInvestor
Mentor
Mentor

Adding to @_gile's advice, there isn't any need for a conditional here:

 

// get the objectId of the 'effective block table record'
var btrId = br.IsDynamicBlock ? br.DynamicBlockTableRecord : br.BlockTableRecord;

You can just use the DynamicBlockTableRecord property, as it returns the correct value in all cases.

0 Likes
Message 5 of 10

Amremad
Collaborator
Collaborator

i don't know how explain hat i need but i will try to discuss if i can 

in first i have dynamic block (A) may be has many anonymous names of some instants of the dynamic block (A)

 

so your code if i use it i must take a new instant of dynamic block (A) and add it to database as a new entity.

so what i need to do 

create function just return the parent of block it self just something like pointer it at TableRecord.

 

for exmple :

i have dynamic block called "Dynamic" has some  anonymous blocks name *U99 ,...., *U50

 

now when i pick the block *U99 just my function return the pure parent object which is called dynamic without add it in database , 

 

i hope u understand me 🙂 

0 Likes
Message 6 of 10

_gile
Consultant
Consultant

As said upper the DynamicBlockTableRecord property of any block reference returns the ObjectId of the source block definition (BlockTableRecord) of dynamic blocs.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 7 of 10

ChrisBatt
Advocate
Advocate

I would return the ObjectID as suggested by others, but you can't just use the dynamic block table to get it as not all anonymous blocks are dynamic which is a common misconception that people seem to have.  This is code that I have to get the true name of a block.  You can modify this to get the ObjectID from the effective block.

 

                //Get the name of the block
                sRetName = blk.Name;
                BlockTableRecord btr = tr.GetObject(blk.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                //check to see if this block is dynamic, which then means that the correct name is in the dynamic block table and not the block name
                if (btr.IsDynamicBlock)
                {
                    sRetName = ((BlockTableRecord)blk.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)).Name;
                }
                //if the block is anonymous but not dynamic, then the name is hidden even deeper and need to get the true name as it still has the *U name
                //to do this we need to go through the XData for the block as it links back to the true original block
                else if (btr.IsAnonymous)
                {
                    //Get the XData to search through
                    var xData = btr.XData.AsArray();
                    for (int i = 0; i < xData.Length; i++)
                    {
                        //Check to see if this is the handle to the original block
                        var tv = xData[i];
                        if (tv.TypeCode == (int)DxfCode.ExtendedDataHandle)
                        {
                            long ln;
                            Handle hn;
                            ObjectId id;
                            // Convert the hexadecimal handle string to 64-bit integer so that we can get the objectID and get the real block which we want the name from
                            ln = Convert.ToInt64(tv.Value.ToString(), 16);
                            // Now create a Handle from the long integer
                            hn = new Handle(ln);
                            // And attempt to get an ObjectId for the Handle
                            id = doc.Database.GetObjectId(false, hn, 0);
                            //Then get the original entity which has the real name for the block
                            BlockTableRecord ent = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                            sRetName = ent.Name;
                        }
                    }
                }

 

0 Likes
Message 8 of 10

ActivistInvestor
Mentor
Mentor

@ChrisBatt wrote:

I would return the ObjectID as suggested by others, but you can't just use the dynamic block table to get it as not all anonymous blocks are dynamic which is a common misconception that people seem to have. 

 


 

What you seem to be suggesting above is not true.

 

The DynamicBlockTableRecord property of the BlockReference for a dynamic block always returns the ObjectId of the BlockTableRecord representing a dynamic block's definition, whose name is the 'effective name" of the BlockReference.

 

If an anonymous block is not associated with a dynamic block, then it has no name (which is why they're called 'anonymous'), so the code you posted is not really applicable for non-dynamic anonymous blocks. For example, an associative array is also represented by an anonymous block insertion, that does not have a 'true name', and contains no handles in it's XData.

 

For any dynamic block reference, the code you show does exactly the same thing that the GetEffectiveName() method shown in the original post above does.

 

Anonymous blocks serve many uses. They can be created by any application for any purpose, and may contain no XData at all.

 

0 Likes
Message 9 of 10

ChrisBatt
Advocate
Advocate

A block can be non-dynamic and anonymous and still lead back to get the effective name of the block.  It is true that if the block is dynamic then you can just look at the dynamic block table record.  However, when the block is non-dynamic and is anonymous and has XData which contains a record to a handle then you can get the effective name this way.  If it does not have XData then it would be a true anonymous block and wouldn't have a name as you said.  I have lots of drawings which support this conclusion as I see this on a regular basis which is why I had to come up with this solution.  

0 Likes
Message 10 of 10

ActivistInvestor
Mentor
Mentor

@ChrisBatt wrote:

A block can be non-dynamic and anonymous and still lead back to get the effective name of the block. 

 

Plain vanilla AutoCAD does not create anonymous, non-dynamic block references containing Xdata with a handle that references another block. 

 

If you have such a case, it must have been created by a third-party application or an AutoCAD vertical solution, which isn't really relavant to the topic. 

 

The other issue is with your code: it scans through all Xdata attached to an object looking for the first handle field, without regards for what application stored it there. Any number of applications can store Xdata on the same object. You have to look at the Xdata for a specific application using its registered application name in order to find something. You also haven't mentioned what that registered application name is in the case of the handle that you are reading from Xdata. If you know what the registered application name is (you're supposed to, because without that all Xdata is ambiguous) then you should be using the GetXdataForApplication() method, rather than the Xdata property. 

 

 

 

0 Likes