BLockReference.AttributeCollection has actual attribute references within!?

BLockReference.AttributeCollection has actual attribute references within!?

oneMSN
Advocate Advocate
4,133 Views
8 Replies
Message 1 of 9

BLockReference.AttributeCollection has actual attribute references within!?

oneMSN
Advocate
Advocate

I've been using this to get access to attributes in a blockReference

 

foreach (ObjectId objectId in block.AttributeCollection)

But today, I exploded a block to get it's child blocks and I wanted to iterate over the child block Attribute collection.

 

But I got an invalid cast exception at the above line.

 

When I looked at the 

block.AttributeCollection

 I found it had actual AttributeReferences inside and not the ObjectIds I was expecting.

 

How on earth are we to deal with a collection which does not know what it is a collection of: a collection of ObjectIds or a collection of actual AttributeReferences?

Anyone come across this and got a good way of dealing with this?

 

0 Likes
Accepted solutions (1)
4,134 Views
8 Replies
Replies (8)
Message 2 of 9

_gile
Consultant
Consultant

Hi,

 

While dealing with nested block attributes, you do no need to explode the parent block, just inspect the parent definition (BlockTable Record) to find the nested block reference and get its attributes.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 9

oneMSN
Advocate
Advocate

Unfortunately that was no use for me.  I needed to know the child block Positions with respect to model space to locate them on the Drawing.  Looking only at the parent definition gives positions relative to parent block.

 

Besides It still leaves the question unanswered. How can the block AttributeCollection be returning unexpected reference entities when every example I've seen shows that I should be expecting ObjectIds in there.

0 Likes
Message 4 of 9

_gile
Consultant
Consultant

You can get the child block reference position within the parent block definition, then just transfrom this postion with the parent block reference BlockTransform property.

 

        private Point3d GetNestedBlockPosition (BlockReference parentBlockRef, string nestedBlockName)
        {
            var tr = parentBlockRef.Database.TransactionManager.TopTransaction;
            var btr = (BlockTableRecord)tr.GetObject(parentBlockRef.BlockTableRecord, OpenMode.ForRead);
            foreach(ObjectId id in btr)
            {
                if (id.ObjectClass.Name == "AcDbBlockReference")
                {
                    var nestedBlockRef = (BlockReference)tr.GetObject(id, OpenMode.ForRead);
                    if (nestedBlockRef.Name == nestedBlockName)
                    {
                        return nestedBlockRef.Position.TransformBy(parentBlockRef.BlockTransform);
                    }
                }
            }
            throw new ArgumentException($"Block {nestedBlockName} not found.");
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 9

ActivistInvestor
Mentor
Mentor

@oneMSN wrote:

 

Besides It still leaves the question unanswered. How can the block AttributeCollection be returning unexpected reference entities when every example I've seen shows that I should be expecting ObjectIds in there.


The Explode() method creates objects that are not in a database, and you must either add them to a database or dispose them. Because those objects are not in a database, they have no ObjectIds, and they are already open, newly-created objects.

 

When you explode a BlockReference that contains other BlockReferences with attributes, the resulting BlockReferences are not database-resident, and so there is no way to get their AttributeReferences using ObjectIds.

 

That's why their AttributeCollections will contain AttributeReferences.

 

If you want to write code that can deal with both database-resident and in-memory BlockReferences with attributes, you can use something like this extension method:

 

public static IEnumerable<AttributeReference> GetAttributes(this BlockReference owner, Transaction trans)
{
   if(owner == null)
      throw new ArgumentNullException("owner");
   if(trans == null)
      throw new ArgumentNullException("trans");
   if(owner.Database != null)
   {
      foreach(ObjectId id in owner.AttributeCollection)
         yield return (AttributeReference) trans.GetObject(id, OpenMode.ForRead);
   }
   else
   {
      foreach(AttributeReference att in owner.AttributeCollection)
         yield return att;
   }
}
Message 6 of 9

ActivistInvestor
Mentor
Mentor
Accepted solution

@Activist_Investor wrote:

If you want to write code that can deal with both database-resident and in-memory BlockReferences with attributes, you can use something like this extension method:

I neglected to notice that the example code above doesn't allow for opening the AttributeReferences for write, so......

 

public static IEnumerable<AttributeReference> GetAttributes(this BlockReference owner, OpenMode mode = OpenMode.ForRead, Transaction trans = null)
{
   if(owner == null)
      throw new ArgumentNullException("owner");
   if(owner.Database != null)
   {
trans = trans ?? owner.Database.TransactionManager.TopTransaction;
if(trans == null)
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions); foreach(ObjectId id in owner.AttributeCollection) yield return (AttributeReference) trans.GetObject(id, mode); } else { foreach(AttributeReference att in owner.AttributeCollection) yield return att; } }

 

Message 7 of 9

ActivistInvestor
Mentor
Mentor

@_gile wrote:

You can get the child block reference position within the parent block definition, then just transfrom this postion with the parent block reference BlockTransform property.

 


Hi @_gile. While the BlockTransform property can be used to transform coordinates or objects from the block's model space to the ECS of the block reference, it cannot account for attributes that have been edited, and have had their position/rotation, etc modified from the 'default' position/rotation, etc.

0 Likes
Message 8 of 9

_gile
Consultant
Consultant

Activist_Investor a écrit :

Hi @_gile. While the BlockTransform property can be used to transform coordinates or objects from the block's model space to the ECS of the block reference, it cannot account for attributes that have been edited, and have had their position/rotation, etc modified from the 'default' position/rotation, etc.


@ActivistInvestor you're right, but it should work with attributes which have position locked.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 9

oneMSN
Advocate
Advocate

Exactly what I needed to know. Great explanation.

0 Likes