Author Topic: Read a blocks attributes with C#  (Read 749 times)

0 Members and 1 Guest are viewing this topic.

sonny3g

  • Newt
  • Posts: 27
Read a blocks attributes with C#
« on: June 07, 2019, 09:39:10 AM »
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;
            }

gile

  • Water Moccasin
  • Posts: 2224
  • Marseille, France
Re: Read a blocks attributes with C#
« Reply #1 on: June 07, 2019, 10:11:33 AM »
You're collecting the the TextString values of the attribute definitions of some block definition, not the the TextString values of the attribute references of the selected block reference.
The first ones are the default values, the second ones the values entered while inserting a block reference or edited later. I guess you want to get the second ones.
If so, you have to iterate through the block reference's AttributeCollection which contains the AttributeReferences you're looking for.
Speaking English as a French Frog

sonny3g

  • Newt
  • Posts: 27
Re: Read a blocks attributes with C#
« Reply #2 on: June 07, 2019, 10:30:43 AM »
Thank you Giles for responding and pointing out the problem.  Your help is always appreciated.

That is correct, I need the text string values in the attributes.  But I also need to know the attribute tag name so I make sure I get the correct values for each of the other functions.

Would I replace AttributeDefinition  with AttributeReference?  And if so, would I still be able to get the attribute Tag?

Sonny


sonny3g

  • Newt
  • Posts: 27
Re: Read a blocks attributes with C#
« Reply #4 on: June 07, 2019, 11:53:24 AM »
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;
            }

        }


Atook

  • Swamp Rat
  • Posts: 933
Re: Read a blocks attributes with C#
« Reply #5 on: June 07, 2019, 12:47:40 PM »
Congrats sonny3g, looks good.

I suggest you might rename your attdef variable to attref while you're thinking about this; or two years down the road, you'll wonder how you can pull the value from the definition. :)

sonny3g

  • Newt
  • Posts: 27
Re: Read a blocks attributes with C#
« Reply #6 on: June 11, 2019, 11:50:48 AM »
Your right Atook, I missed that.  Thanks for the catch.

pjm8765

  • Newt
  • Posts: 46
Re: Read a blocks attributes with C#
« Reply #7 on: June 18, 2019, 09:12:38 AM »
So, there always has to be at least one block reference for each block (and corresponding attribute reference attached to it for each attribute definition), yes?  It's implied in the various examples and tutorials I have seen, but I haven't seen it openly stated thus far.

gile

  • Water Moccasin
  • Posts: 2224
  • Marseille, France
Re: Read a blocks attributes with C#
« Reply #8 on: June 18, 2019, 09:23:00 AM »
So, there always has to be at least one block reference for each block (and corresponding attribute reference attached to it for each attribute definition), yes?  It's implied in the various examples and tutorials I have seen, but I haven't seen it openly stated thus far.

No, you can have non referenced block definitions (the ones you can purge) and block references may not have the same attribute references as the attribute definitions in the block definition (this is corrected by the ATTSYNC command).
Speaking English as a French Frog

dgorsman

  • Water Moccasin
  • Posts: 2394
Re: Read a blocks attributes with C#
« Reply #9 on: June 19, 2019, 03:24:10 PM »
A block definition cannot be purged if there's another hard pointer to it other than a definition.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

pjm8765

  • Newt
  • Posts: 46
Re: Read a blocks attributes with C#
« Reply #10 on: June 20, 2019, 04:35:17 AM »
Thanks for your responses.  I actually asked the wrong question.  I meant to ask whether we had to create a block reference in order to be able to store an attribute value (as opposed to just storing the value in the block).  In this particular case I am storing the attributes of a "job" (aka order, aka project) and only need one instance of it.  So I was hoping to just have to work with the block record without having to create a block reference. 

Anyway, I came back to it this morning, with refreshed eyes and brain, and have been able to sort this out...so the answer to my revised question is yes, I can just use a block without any block references.  That does leave that block open to being purged, however, so it's not perfect. But we have other AutoCAD automation software (COM based not .NET) that does the same and that doesn't suffer from any issues.

n.yuan

  • Bull Frog
  • Posts: 275
Re: Read a blocks attributes with C#
« Reply #11 on: June 20, 2019, 09:46:45 AM »
Thanks for your responses.  I actually asked the wrong question.  I meant to ask whether we had to create a block reference in order to be able to store an attribute value (as opposed to just storing the value in the block).  In this particular case I am storing the attributes of a "job" (aka order, aka project) and only need one instance of it.  So I was hoping to just have to work with the block record without having to create a block reference. 

Anyway, I came back to it this morning, with refreshed eyes and brain, and have been able to sort this out...so the answer to my revised question is yes, I can just use a block without any block references.  That does leave that block open to being purged, however, so it's not perfect. But we have other AutoCAD automation software (COM based not .NET) that does the same and that doesn't suffer from any issues.

I think you are still wrong by giving "yes" to your "revised question". If you do not need a Block reference (to be inserted in the drawing visibly), then you are "abuse" a block definition - you use it to store some data (per document, in this case) that does not need to be seen and can be lost unnoticed/unintentionally (by purging). You should use different approaches for storing data that is not related to AutoCAD entities, such as NamedDictionary, or ExtensionDictionary/XData attached to ModelSpace block definition... depending on the nature of data.

Atook

  • Swamp Rat
  • Posts: 933
Re: Read a blocks attributes with C#
« Reply #12 on: June 20, 2019, 12:07:19 PM »
Yeah, sounds like it'd be best stored on the NOD.

I'm curious about Daniel's comment about storing via XData on the ModelSpace block. What would be a good reason to store data there instead of the NOD?

CADbloke

  • Bull Frog
  • Posts: 307
  • Crash Test Dummy
Re: Read a blocks attributes with C#
« Reply #13 on: June 21, 2019, 07:47:56 PM »
Storing XData in a block with Attributes is a bad idea, it will get wiped when the block is ATTSYNCed: http://www.theswamp.org/index.php?topic=44123.msg494410#msg494410

Also, when retrieving attribute references, you need to also retrieve the Attribute definitions from the BlockTableRecord for any Constant attributes as there are no references for these in a block reference. This is a way to store immutable data in a block reference though, it can only be changed in the block definintion (BlockTableRecord ) and does not appear in the EATTEDIT editor.

A single line attribute may store 255 characters, any more than that will crash AutoCAD. Try it, keep adding text in a block edit over the limit and see what happens, it's actually seemingly unpredictable and AutoCAD doesn't seem to police it, the crash looks like a classic buffer overrun smash. MText Attributes can hold around 27963 characters. They are the limits I set in CAD replace.

In answer to your problem, Norman has nailed it. Think about what the actual data is you are storing, is it per drawing or per thing in the drawing?