Author Topic: Easy way to get attributes from blocks  (Read 8149 times)

0 Members and 1 Guest are viewing this topic.

GILESP

  • Newt
  • Posts: 42
Easy way to get attributes from blocks
« on: August 23, 2012, 06:02:25 AM »
I'm working on my first grown up VB.net project, which is a rewrite of a vba script that collects certain attribute values from blocks and tabulates them as a BOM.

I'm struggling to get attribute values from block references - I'm compounded by the issue that whilst most of the attributes are constant, there are cases where they aren't  so I really need to collect all attributes and sort by tag.

My VBA approach used the BlockRef.Getattributes and the BlockRef.GetConstantAttributes method which put everything I needed in a data structure for me.

My VB method for getting attributes differs depending of if it's contant: for non constant attributes I'm using blockref.AttributeCollection statement which then give me a structure containing attributes, very much as per VBA

However for constant attributes, I'm currently having to go to the block definition, and recurse through the contents, isolating attribute definitions and checking if the constant property is true

These are 2 pretty different approaches, and are upsetting my programing mojo a bit - is there one technique that can be used for both types of attribute?

Any thoughts, tips or code fragments would be appreciated..

G
..END OF LINE..

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Easy way to get attributes from blocks
« Reply #1 on: August 23, 2012, 07:54:46 AM »
blockref.AttributeCollection will return all AttributeReferences associated with the BlockReference this includes any marked as constant.
Revit 2019, AMEP 2019 64bit Win 10

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Easy way to get attributes from blocks
« Reply #2 on: August 23, 2012, 08:06:52 AM »
Perhaps ;
Make this .NET forum current and search for AttributeCollection
... you'll probably find a couple of days reading :)
 
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

GILESP

  • Newt
  • Posts: 42
Re: Easy way to get attributes from blocks
« Reply #3 on: August 23, 2012, 08:38:25 AM »
blockref.AttributeCollection will return all AttributeReferences associated with the BlockReference this includes any marked as constant.

Really? It is not in my case.  Are there conditions in which it doesn't return a attribute reference? Perhaps hidden ones? (they're hidden in my example).


Kerry - have tried the search feature again - you're right, many days reading..  :-o
..END OF LINE..

TheMaster

  • Guest
Re: Easy way to get attributes from blocks
« Reply #4 on: August 23, 2012, 06:30:09 PM »
I'm working on my first grown up VB.net project, which is a rewrite of a vba script that collects certain attribute values from blocks and tabulates them as a BOM.

I'm struggling to get attribute values from block references - I'm compounded by the issue that whilst most of the attributes are constant, there are cases where they aren't  so I really need to collect all attributes and sort by tag.

My VBA approach used the BlockRef.Getattributes and the BlockRef.GetConstantAttributes method which put everything I needed in a data structure for me.

My VB method for getting attributes differs depending of if it's contant: for non constant attributes I'm using blockref.AttributeCollection statement which then give me a structure containing attributes, very much as per VBA

However for constant attributes, I'm currently having to go to the block definition, and recurse through the contents, isolating attribute definitions and checking if the constant property is true

These are 2 pretty different approaches, and are upsetting my programing mojo a bit - is there one technique that can be used for both types of attribute?

Any thoughts, tips or code fragments would be appreciated..

G

Sorry for the C#, me no speak VB  :grin:

Code - C#: [Select]
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using Autodesk.AutoCAD.Runtime;
  7.  
  8. namespace Autodesk.AutoCAD.DatabaseServices
  9. {
  10.    public static class AcDbExtensionMethods
  11.    {
  12.       static RXClass attDefClass = RXClass.GetClass( typeof( AttributeDefinition ) );
  13.  
  14.       // Requires a transaction (not an OpenCloseTransaction) to be active when called:
  15.       // Returns an enumeration of all AttributeDefinitions whose Constant property is
  16.       // true, and all AttributeReferences attached to the block reference.
  17.  
  18.       public static IEnumerable<DBText> GetAttributes( this BlockReference blockRef )
  19.       {
  20.          Transaction tr = blockRef.GetTransaction();
  21.          BlockTableRecord btr = (BlockTableRecord) blockRef.DynamicBlockTableRecord.GetObject( OpenMode.ForRead );
  22.          if( btr.HasAttributeDefinitions )
  23.          {
  24.             foreach( ObjectId id in blockRef.AttributeCollection )
  25.             {
  26.                yield return (AttributeReference) tr.GetObject( id, OpenMode.ForRead );
  27.             }
  28.             foreach( ObjectId id in btr )
  29.             {
  30.                if( id.ObjectClass == attDefClass )
  31.                {
  32.                   AttributeDefinition attdef = (AttributeDefinition) tr.GetObject( id, OpenMode.ForRead );
  33.                   if( attdef.Constant )
  34.                      yield return attdef;
  35.                }
  36.             }
  37.          }
  38.       }
  39.  
  40.       // Requires an active transaction (not an OpenCloseTransaction)
  41.       // Returns a dictionary whose values are either constant AttributeDefinitions
  42.       // or AttributeReferences, keyed to their tags:
  43.  
  44.       public static Dictionary<string, DBText> GetAttributeDictionary( this BlockReference blockref )
  45.       {
  46.          return blockref.GetAttributes().ToDictionary( a => GetTag( a ), StringComparer.OrdinalIgnoreCase );
  47.       }
  48.  
  49.       public static Transaction GetTransaction( this DBObject obj )
  50.       {
  51.          if( obj.Database == null )
  52.             throw new ArgumentException( "No database" );
  53.          Transaction tr = obj.Database.TransactionManager.TopTransaction;
  54.          if( tr == null )
  55.             throw new InvalidOperationException( "No active transaction" );
  56.          return tr;
  57.       }
  58.  
  59.       static string GetTag( DBText dbtext )
  60.       {
  61.          AttributeDefinition attdef = dbtext as AttributeDefinition;
  62.          if( attdef != null )
  63.             return attdef.Tag;
  64.          AttributeReference attref = dbtext as AttributeReference;
  65.          if( attref != null )
  66.             return attref.Tag;
  67.          throw new ArgumentException( "requires an AttributeDefintion or AttributeReference" );
  68.       }
  69.    }
  70. }
  71.  
« Last Edit: August 23, 2012, 07:37:33 PM by TT »

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Easy way to get attributes from blocks
« Reply #5 on: March 09, 2016, 06:01:41 PM »
Like most things The Master left for us on this wonderful Internet thing we have, I have found this code useful. More than 1500 people have looked at this so I guess others find it useful too. I have broken tweaked it over the years so I thought I should share what I have come up with - it is basically the same as Tony's original with XML doc comments.

Code - C#: [Select]
  1. // Another Tony Tanzillo Classic
  2. // http://www.theswamp.org/index.php?topic=42591.msg477887#msg477887
  3.  
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using Autodesk.AutoCAD.Runtime;
  8. using AcDb = Autodesk.AutoCAD.DatabaseServices;
  9. using AcRx = Autodesk.AutoCAD.Runtime;
  10.  
  11. namespace Autodesk.AutoCAD.DatabaseServices
  12. {
  13.     /// <summary> <c>Autodesk.AutoCAD.DatabaseServices</c> Extension methods mostly by Tony Tanzillo. </summary>
  14.     public static class AcDbExtensionMethods
  15.     {
  16.         private static readonly RXClass attDefClass = RXObject.GetClass(typeof (AttributeDefinition));
  17.  
  18.         /// <summary> Requires a transaction (not an OpenCloseTransaction) to be active when called:
  19.         ///     <para>Returns an enumeration of all <c>AttributeReference</c>s attached to the <c>BlockReference</c></para>
  20.         ///           plus all all <c>AttributeDefinition</c>s whose Constant property is true. </summary>
  21.         public static IEnumerable<DBText> GetAttributes(this BlockReference blockRef)
  22.         {
  23.             Transaction tr = blockRef.GetTransaction();
  24.             var btr = (BlockTableRecord)blockRef.DynamicBlockTableRecord.GetObject(OpenMode.ForRead);
  25.             if (btr.HasAttributeDefinitions)
  26.             {
  27.                 foreach (ObjectId id in blockRef.AttributeCollection)
  28.                 {
  29.                     yield return (AttributeReference)tr.GetObject(id, OpenMode.ForRead);
  30.                 }
  31.                 foreach (ObjectId id in btr)
  32.                 {
  33.                     if (id.ObjectClass == attDefClass)
  34.                     {
  35.                         var attdef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  36.                         if (attdef.Constant)
  37.                             yield return attdef;
  38.                     }
  39.                 }
  40.             }
  41.         }
  42.  
  43.         // adapted from the above by CADbloke
  44.         /// <summary> Requires a transaction (not an OpenCloseTransaction) to be active when called:
  45.         ///     <para>Gets all the <c>AttributeReference</c>s from a <c>BlockReference</c></para>
  46.         ///           Does not get any Constant Attributes.</summary>
  47.         /// <param name="blockRef"> The BlockReference to get Attributes from. </param>
  48.         /// <returns> An enumerator that allows foreach to be used to process the attribute references in this collection. </returns>
  49.         public static IEnumerable<AttributeReference> GetAttributeReferences(this BlockReference blockRef)
  50.         {
  51.             Transaction tr = blockRef.GetTransaction();
  52.             var btr = (BlockTableRecord)blockRef.DynamicBlockTableRecord.GetObject(OpenMode.ForRead); // Dynamic?
  53.             if (btr.HasAttributeDefinitions)
  54.             {
  55.                 foreach (ObjectId id in blockRef.AttributeCollection)
  56.                 {
  57.                     yield return (AttributeReference)tr.GetObject(id, OpenMode.ForRead);
  58.                 }
  59.             }
  60.         }
  61.  
  62.         // adapted from the above by CADbloke
  63.         /// <summary> Requires a transaction (not an OpenCloseTransaction) to be active when called:
  64.         ///     <para>Gets all the <c>Constant</c> Attributes (<c>AttributeDefinition</c>s that are Constants) from a Block Reference. </para>
  65.         ///           Does not get any <c>AttributeReference</c>s.</summary>
  66.         public static IEnumerable<AttributeDefinition> GetAttributeConstants(this BlockReference blockRef)
  67.         {
  68.             Transaction tr = blockRef.GetTransaction();
  69.             var btr = (BlockTableRecord)blockRef.DynamicBlockTableRecord.GetObject(OpenMode.ForRead); //  Dynamic?
  70.             if (btr.HasAttributeDefinitions)
  71.             {
  72.                 foreach (ObjectId id in btr)
  73.                 {
  74.                     if (id.ObjectClass == attDefClass)
  75.                     {
  76.                         var attdef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  77.                         if (attdef.Constant)
  78.                             yield return attdef;
  79.                     }
  80.                 }
  81.             }
  82.         }
  83.  
  84.         // adapted from the above by CADbloke
  85.         /// <summary> Requires a transaction (not an OpenCloseTransaction) to be active when called:
  86.         ///     <para>Gets all of the <c>AttributeDefinition</c>s from a <c>BlockReference</c>.</para> </summary>
  87.         public static IEnumerable<AttributeDefinition> GetAttributeDefinitions(this BlockReference blockRef)
  88.         {
  89.             Transaction tr = blockRef.GetTransaction();
  90.             var btr = (BlockTableRecord)blockRef.DynamicBlockTableRecord.GetObject(OpenMode.ForRead);
  91.             if (btr.HasAttributeDefinitions)
  92.             {
  93.                 foreach (ObjectId id in btr)
  94.                 {
  95.                     if (id.ObjectClass == attDefClass)
  96.                     {
  97.                         var attdef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  98.                         /*if (attdef.Constant)*/
  99.                         {
  100.                             yield return attdef;
  101.                         }
  102.                     }
  103.                 }
  104.             }
  105.         }
  106.  
  107.         /// <summary> Requires an active transaction (not an OpenCloseTransaction).
  108.         ///     <para />Returns a dictionary whose values are either constant <c>AttributeDefinition</c>s
  109.         ///     or <c>AttributeReference</c>s, keyed to their tags: </summary>
  110.         public static Dictionary<string, DBText> GetAttributesToDictionaryKeyByTag(this BlockReference blockref)
  111.         {
  112.             return blockref.GetAttributes().ToDictionary(a => GetTag(a), StringComparer.OrdinalIgnoreCase);
  113.         }
  114.  
  115.  
  116.  
  117.         /// <summary> Gets the <c>Database.TransactionManager.TopTransaction</c>. </summary>
  118.         /// <exception cref="ArgumentNullException"> No database </exception>
  119.         /// <exception cref="Autodesk.AutoCAD.Runtime.Exception"> Requires an active transaction (NOT an OpenCloseTransaction) </exception>
  120.         public static Transaction GetTransaction(this DBObject obj)
  121.         {
  122.             if (obj.Database == null)
  123.                 throw new ArgumentNullException("obj", "No Database");
  124.  
  125.             Transaction tr = obj.Database.TransactionManager.TopTransaction;
  126.             if (tr == null)
  127.                 throw new AcRx.Exception(AcRx.ErrorStatus.NoActiveTransactions);
  128.  
  129.             return tr;
  130.         }
  131.  
  132.         /// <summary> Gets the tag of an <c>AttributeDefinition</c> or <c>AttributeReference</c>. </summary>
  133.         /// <exception cref="ArgumentException"> requires an AttributeDefintion or AttributeReference. </exception>
  134.         /// <param name="dbtext"> <c>AttributeDefinition</c> or <c>AttributeReference</c> ONLY. </param>
  135.         /// <returns> The tag. </returns>
  136.         private static string GetTag(DBText dbtext)
  137.         {
  138.             var attdef = dbtext as AttributeDefinition;
  139.             if (attdef != null)
  140.                 return attdef.Tag;
  141.             var attref = dbtext as AttributeReference;
  142.             if (attref != null)
  143.                 return attref.Tag;
  144.             throw new ArgumentException("requires an AttributeDefintion or AttributeReference");
  145.         }
  146.     }
  147. }