Need to read the attributes in a selected block.

Need to read the attributes in a selected block.

sonny3g
Collaborator Collaborator
1,089 Views
12 Replies
Message 1 of 13

Need to read the attributes in a selected block.

sonny3g
Collaborator
Collaborator
I need to be able to select a single block and read it's attributes.  I am using a structure to then pass those attribute values to other functions.  So far I have not been successful in reading the attributes and returning a string value.  Below is my code.  Any help is appreciated.

            using (Transaction rbTrans = db.TransactionManager.StartTransaction())
            {
                BlockReference bref = (BlockReference)rbTrans.GetObject(bed, OpenMode.ForRead);
                BlockTableRecord bdef = (BlockTableRecord)rbTrans.GetObject(bref.BlockTableRecord, OpenMode.ForRead);
                if (bdef.HasAttributeDefinitions != true) return null;
                foreach (ObjectId id in bdef)
                    {
                    DBObject obj = (DBObject)rbTrans.GetObject(id, OpenMode.ForRead, true);
                    AttributeDefinition attdef = obj as AttributeDefinition;
                   
                    if ((attdef != null) && (!attdef.Constant))
                    {
                        if (attdef.Tag == "pos_Origin_Z")
                        {
                            structure.insPtZ = attdef.TextString;
                        }
                        else if ((attdef.Tag == "pos_Endpoint_Z"))
                        {
                            structure.endPtZ = attdef.TextString;
                        }
                        else if ((attdef.Tag == "prd_UL"))
                        {
                            structure.uLabel = attdef.TextString;
                        }
                        else if ((attdef.Tag == "prd_LP"))
                        {
                            structure.layPos = attdef.TextString;
                        }
                    }
                }
                structure.insPt = bref.Position;
                structure.blkName = ((BlockTableRecord)bref.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)).Name;
                structure.lyrName = bref.Layer;
                structure.rotAngle = bref.Rotation;
                
                rbTrans.Dispose();

                return structure;
            }
 
Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Accepted solutions (1)
1,090 Views
12 Replies
Replies (12)
Message 2 of 13

norman.yuan
Mentor
Mentor
Accepted solution

When you say "read the attributes in a selected block", do you mean AttributeDefinition in a block definition (BlockTableRecord), or do you mean AttributeReferences in BlockReference? Your code gets all AttributeDefinitions and the default Attribute value, yet you collect block position/layer/rotation from a block reference and store the data from different object (block definition and block reference) in a struc for later use.

 

I suspect what you really meant is attributes (AttributeReference) in a block reference. While a BlockReference is a reference to a block definition, the attributes in BlockReference is not a reference to AttributeDefinition. AttributeDefinition is like object template, which is used to create AttributeReference in BlockReference. With code, one can decide if an attributereference is needed in a BlockReference. If yes, the attributeReference can either be created based on available AttributeDefinition, or be created without using AttributeDefinition as template at all. So, If your need get attribute values of a BlockReference, you DO NOT need to trace back to the block definition. Simply loop through BlockReference.AttributeCollection property:

 

private MyStructure GetDataFromBlockReference(ObjectId blockReferenceId)

{

    var structure = new MyStructure();

    using (var tran=blockReferenceId.Database.TransactionManager.StartTransaction())

    {

        var blk=(BlockReference)tran.GetObject(blockReferneceId, OpenMode.ForRead)

        structure.insPt=blk.Position

        //if the block is a dynamic block, you may need to trace back to the dynamic block definition for its name

        structure.blkName=blk.Name; 

        structure.Rotation=....

        foreach (ObjectId id in blk.AttributeCollection)

        (

             var att=(AttributeReference)tran.GetObject(id,OpenMode.ForRead)

             switch (att.Tag.TpUpper)

             {

                 case "POS__ORIGIN_Z":

                      structure.insPtz =COnvert.ToDouble(att.TextString);

                     break;

                 case .....

             }

        )

        tran.Commit();

    }

    return structure

}

 

HTH

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 13

sonny3g
Collaborator
Collaborator

Thanks Atook and Giles. As usual, you both are very helpful! Between the two of you and Norman Yuan, I was able to get this portion to work now. This is what I have and it is much more concise than my first try. Structure is a separate function that will be used by other functions later.

 

public BlkStrct ReadBlock(ObjectId bed)
{
using (Transaction rbTrans = db.TransactionManager.StartTransaction())
{
BlockReference bref = (BlockReference)rbTrans.GetObject(bed, OpenMode.ForRead);
BlockTableRecord bdef = (BlockTableRecord)rbTrans.GetObject(bref.DynamicBlockTableRecord, OpenMode.ForRead);
if (bdef.HasAttributeDefinitions != true) return null;
foreach (ObjectId id in bref.AttributeCollection)
{
AttributeReference attdef = (AttributeReference)rbTrans.GetObject(id, OpenMode.ForRead);
switch (attdef.Tag)
{
case "pos_Origin_Z":
structure.insPtZ = attdef.TextString;
break;
case "pos_Endpoint_Z":
structure.endPtZ = attdef.TextString;
break;
case "prd_UL":
structure.uLabel = attdef.TextString;
break;
case "prd_LP":
structure.layPos = attdef.TextString;
break;
}
}
structure.insPt = bref.Position;
structure.blkName = ((BlockTableRecord)bref.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)).Name;
structure.lyrName = bref.Layer;
structure.rotAngle = bref.Rotation;

rbTrans.Dispose();

return structure;
}

}

 

 

Sonny

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Message 4 of 13

norman.yuan
Mentor
Mentor

Well, this code should be OK in NORMAL/MOST situation, but I'd not test BlockTableRecord.HasAttributDefinitions and return if it is False.

 

As I stated, BlockReference's AttributeReference may or may not have coresponding/matching AttributeDefinition in the block definition (that is, attribute in BlockReference IS NOT A REFERENCE of AttributeDefinition, in spite of its name being "AttributeReference"). Regardless a block definition having AttributeDefinitions or not, a BlockReference can have 0 or multiple AttributeReferences. In rare case, the block reference in interest may or may not have the same attributes as the block definition defines. So, for your code, you do not need to test BlockTableRecord.HasAttributeDefinitions; and in rare case, it may lead to wrong result (if the block definition does not have attribute defined, but somehow the block reference in interest does have attributes (created programmatically). So, in your code, simply loop through BlockReference.AttributeCollection is enough.

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 13

sonny3g
Collaborator
Collaborator

I think I see what you are getting at.  This function is part of a tool that is designed to work on specific blocks that our company has designed and not to work on any others, so, the block must have attributes or the program will kick you out.  

 

I will probably wind up developing a filter that prevents selecting non-company entities and that would eliminate the attribute test altogether.

 

Thank you Norman.

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Message 6 of 13

norman.yuan
Mentor
Mentor

Well, by saying "so, the block must have attributes or the program will kick you out...." I think you still missed the point of what I have said: BlockTableRecord.HasAttributeDefinitions DOES NOT GARANTEE of telling you whether a BlockReference contains AttributeReferences or not. So, the code to test it is COMPLETELY NOT NECESSARY, not to mention the rare chance of leading to wrong result (even the chance is one of ten million, it is a chance!).

 

Even if it is harmless because of the way your specific block reference to be added into drawing (that is, if the block definition has attribute defined, then the block reference ALWAYS has the corresponding attribute references), that line of code is still is not necessary. Just as if you add something like

If (1 != 1) return

into in your code here and there a few times: it may be harmless, by why you need it?

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 13

dgorsman
Consultant
Consultant

Is that a Dispose() being called inside a Using { } for the transaction?

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


0 Likes
Message 8 of 13

kerry_w_brown
Advisor
Advisor

@dgorsman wrote:

Is that a Dispose() being called inside a Using { } for the transaction?


 

 

Good eye David.

Yes, it's superfluous because of the using statement block enclosing it.

 

I casually wondered about the reasoning for the name of the Transaction variable ; rbTrans

... but that's probably just me.

 


// Called Kerry or kdub in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect. ~ kdub
Sometimes the question is more important than the answer. ~ kdub

NZST UTC+12 : class keyThumper<T> : Lazy<T>;      another  Swamper
0 Likes
Message 9 of 13

sonny3g
Collaborator
Collaborator

I am not sure how to determine if my block actually has attributes then.  What is the best way to do that?

 

 

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Message 10 of 13

sonny3g
Collaborator
Collaborator

Yes it is.  When I had the dispose outside of the }, I did not get the expected results for this transaction.  Not sure why.

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Message 11 of 13

sonny3g
Collaborator
Collaborator

rbTrans is how I know what my transaction is for.  In this case, Read Block transaction.  Other functions will have transactions named like dlTrans for dimension leader transaction.  Just how I try to keep track of where I am at and what I am trying to accomplish within a transaction.

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes
Message 12 of 13

norman.yuan
Mentor
Mentor

BlockRefernce.AttributeCollection tells you if there is AttributeReference in the Block reference. Since your code does LOOPING through AttributeCollection, why do you need to worry whether the block (the BlockRefernce!) has attribute or not?

 

AGAIN, BlockTableRecord.HasAttributeDefinitions DOES NOT GUARANTEE a block reference has exactly the same attributes as the block definition defines, even in most/usual scenarios it is the case. In your code, you pass a BlcokRefernce's ID to the subroutine for operation (reading Attribute values), which means you have already selected/identified the block reference (by use picking, or by block name...), it is COMPLETELY no need to test BlockTableRecord.HasAttributeDefinitions. In best case, if is harmless, but meaningless.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 13 of 13

sonny3g
Collaborator
Collaborator

I thought it would be better and quicker for my program to validate that the attributes existed before sending the blockreference to the subroutine to read the attributes.   

 

If I understand what you are telling me though, my requirement is met by the subroutine reading the attributes.  

Scott G. Sawdy
scott.sawdy@bluecoyotecad.com
0 Likes