Author Topic: Getting all blocks in a drawing based on name.  (Read 8677 times)

0 Members and 1 Guest are viewing this topic.

tik

  • Guest
Getting all blocks in a drawing based on name.
« on: April 10, 2013, 09:19:09 AM »
I want to get all the blocks in a drawing without looping through all the entities and check for the type and name. All the blocks I created start with the same name. Is there a easy way to do this ?

Thanks in advance.

Tik.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Getting all blocks in a drawing based on name.
« Reply #1 on: April 10, 2013, 09:55:40 AM »
BlockTableRecord has methods GetBlockReferenceIds which gets all standard blocks. If it's dynamic you'll also need GetAnonymousBlockIds to get the anonymous definitions and then call GetBlockReferenceIds on each of them

tik

  • Guest
Re: Getting all blocks in a drawing based on name.
« Reply #2 on: April 10, 2013, 09:57:31 AM »
Thanks for reply, i was hoping that there is something more like a linq query that i can run on the list directly.

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
Re: Getting all blocks in a drawing based on name.
« Reply #3 on: April 10, 2013, 10:22:39 AM »
You can create a selection filter for a type and a name, and everything else like layers.

Code: [Select]
Dim tv() As TypedValue = {New TypedValue(DxfCode.Start, "INSERT"),
      New TypedValue(DxfCode.BlockName, "MyBlockName"),
      New TypedValue(DxfCode.LayerName, "MyLayerName")}
Dim selFilter As New SelectionFilter(tv)
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

tik

  • Guest
Re: Getting all blocks in a drawing based on name.
« Reply #4 on: April 10, 2013, 10:31:15 AM »
Huiz,

Thanks for the suggestion, that is what I am looking for, is it possible to have a wild card for block names instead of matching it to a specific Block Name for example  I want to get all the blocks starts with "MyBlock*" instead of "MyBlock".

Regards
Tik,

BlackBox

  • King Gator
  • Posts: 3770
Re: Getting all blocks in a drawing based on name.
« Reply #5 on: April 10, 2013, 10:32:23 AM »
You can create a selection filter for a type and a name, and everything else like layers.

Code: [Select]
Dim tv() As TypedValue = {New TypedValue(DxfCode.Start, "INSERT"),
      New TypedValue(DxfCode.BlockName, "MyBlockName"),
      New TypedValue(DxfCode.LayerName, "MyLayerName")}
Dim selFilter As New SelectionFilter(tv)

Alexander (, and perhaps DiningPhilosopher?) actually educated me over at Autodesk Discussion Group, that it is often better to iterate the Database rather than using a SelectionFilter, as the code could be used in both MdiActiveDrawing, and batch processing... Further, depending on the system specs, there's not much speed difference.
"How we think determines what we do, and what we do determines what we get."

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
Re: Getting all blocks in a drawing based on name.
« Reply #6 on: April 10, 2013, 10:35:11 AM »
Huiz,

Thanks for the suggestion, that is what I am looking for, is it possible to have a wild card for block names instead of matching it to a specific Block Name for example  I want to get all the blocks starts with "MyBlock*" instead of "MyBlock".

Regards
Tik,

Yes it is.
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

BlackBox

  • King Gator
  • Posts: 3770
Re: Getting all blocks in a drawing based on name.
« Reply #8 on: April 10, 2013, 10:54:17 AM »
Alexander (, and perhaps DiningPhilosopher?) actually educated me over at Autodesk Discussion Group, that it is often better to iterate the Database rather than using a SelectionFilter, as the code could be used in both MdiActiveDrawing, and batch processing... Further, depending on the system specs, there's not much speed difference.

I found the post, it was actually DiningPhilosopher who educated me, and Alexander who chimed in toward the end:

Quote from: DiningPhilosopher
if you're working in the drawing editor and operating on objects in the current space, then it makes sense to use SelectAll() with a filter. Of course, sometimes we want our code to work with databases that aren't open in the editor, which precludes using object selection or selection filtering, and requires that we iterate over BlockTableRecord objects directly and operate on those objects of interest, as Alex's code does. So, the answer is 'It depends".
"How we think determines what we do, and what we do determines what we get."

tik

  • Guest
Re: Getting all blocks in a drawing based on name.
« Reply #9 on: April 10, 2013, 11:24:39 AM »
Thanks a lot for the link. That is what i am looking for.

Tik

TheMaster

  • Guest
Re: Getting all blocks in a drawing based on name.
« Reply #10 on: April 10, 2013, 12:08:01 PM »
Thanks a lot for the link. That is what i am looking for.

Tik

Here's a few more from AcDbLinq.cs (not tested after removing some dependencies on other stuff).

Just call database.GetBlockReferences( "pattern" )

Code - C#: [Select]
  1. /// Excerpts from AcDbLinq.cs  Copyright (c)  2008-2013 Tony Tanzillo
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.Internal;
  9. using System.Collections;
  10.  
  11. namespace Autodesk.AutoCAD.DatabaseServices
  12. {
  13.    public static class AcDbLinqExtensions
  14.    {
  15.  
  16.       /// <summary>
  17.       ///
  18.       /// Returns an object that enumerates all references
  19.       /// to all blocks whose names match the given wildcard
  20.       /// pattern, open for read.
  21.       ///
  22.       /// Requires an active transaction (NOT an OpenCloseTransaction)
  23.       ///
  24.       /// </summary>
  25.       ///
  26.  
  27.       public static IEnumerable<BlockReference> GetBlockReferences(
  28.          this Database database,
  29.          string pattern )
  30.       {
  31.          if( string.IsNullOrWhiteSpace( pattern ) )
  32.             throw new ArgumentException( "Requires a wildcard pattern" );
  33.          if( database == null )
  34.             throw new ArgumentNullException( "database" );
  35.          Transaction tr = database.TransactionManager.TopTransaction;
  36.          if( tr == null )
  37.             throw new Autodesk.AutoCAD.Runtime.Exception(
  38.                ErrorStatus.NoActiveTransactions );
  39.          BlockTable blockTable = (BlockTable)
  40.             tr.GetObject( database.BlockTableId, OpenMode.ForRead );
  41.          return blockTable.Cast<ObjectId>()
  42.             .GetObjects<BlockTableRecord>()
  43.             .Where( btr => Utils.WcMatchEx( btr.Name, pattern, true ) )
  44.             .SelectMany( btr => btr.GetBlockReferences() );
  45.       }
  46.  
  47.          /// <summary>
  48.          /// Gets an object that enumerates all references to the given
  49.          /// BlockTableRecord, including references to anonymous dynamic
  50.          /// BlockTableRecords, open for read.
  51.          /// </summary>
  52.  
  53.       public static IEnumerable<BlockReference> GetBlockReferences(
  54.          this BlockTableRecord btr,
  55.          OpenMode mode = OpenMode.ForRead,
  56.          bool directOnly = true )
  57.       {
  58.          if( btr == null )
  59.             throw new ArgumentNullException( "btr" );
  60.          var tr = btr.Database.TransactionManager.TopTransaction;
  61.          if( tr == null )
  62.             throw new InvalidOperationException( "No transaction" );
  63.          var ids = btr.GetBlockReferenceIds( directOnly, true );
  64.          int cnt = ids.Count;
  65.          for( int i = 0; i < cnt; i++ )
  66.          {
  67.             yield return (BlockReference)
  68.                tr.GetObject( ids[i], mode, false, false );
  69.          }
  70.          if( btr.IsDynamicBlock )
  71.          {
  72.             BlockTableRecord btr2 = null;
  73.             var blockIds = btr.GetAnonymousBlockIds();
  74.             cnt = blockIds.Count;
  75.             for( int i = 0; i < cnt; i++ )
  76.             {
  77.                btr2 = (BlockTableRecord) tr.GetObject( blockIds[i],
  78.                   OpenMode.ForRead, false, false );
  79.                ids = btr2.GetBlockReferenceIds( directOnly, true );
  80.                int cnt2 = ids.Count;
  81.                for( int j = 0; j < cnt2; j++ )
  82.                {
  83.                   yield return (BlockReference)
  84.                      tr.GetObject( ids[j], mode, false, false );
  85.                }
  86.             }
  87.          }
  88.       }
  89.  
  90.       /// <summary>
  91.       /// The one, but not only GetObjects<T> (abridged), with due credits
  92.       /// to Jeff H, kaefer, Gile, et. al.
  93.       ///
  94.       /// Returns an object that enumerates all instances of the
  95.       /// given generic argument type, opened accoring to the given
  96.       /// parameters.
  97.       /// </summary>
  98.  
  99.       public static IEnumerable<T> GetObjects<T>(
  100.           this IEnumerable<ObjectId> ids,
  101.           OpenMode mode = OpenMode.ForRead,
  102.           bool openErased = false,
  103.           bool forceOpenOnLockedLayers = false ) where T : DBObject
  104.       {
  105.          if( ids.Any() )
  106.          {
  107.             TransactionManager tm = ids.First().Database.TransactionManager;
  108.  
  109.             RXClass rxclass = RXClass.GetClass( typeof( T ) );
  110.             if( typeof( T ) == typeof( DBObject ) )
  111.             {
  112.                foreach( ObjectId id in ids )
  113.                {
  114.                   yield return (T) tm.GetObject( id, mode, openErased,
  115.                      forceOpenOnLockedLayers );
  116.                }
  117.             }
  118.  
  119.             else if( rxclass.GetHasChildren() )
  120.             {
  121.                foreach( ObjectId id in ids )
  122.                {
  123.                   if( id.ObjectClass.IsDerivedFrom( rxclass ) )
  124.                      yield return (T) tm.GetObject( id, mode, openErased,
  125.                         forceOpenOnLockedLayers );
  126.                }
  127.             }
  128.             else
  129.             {
  130.                foreach( ObjectId id in ids )
  131.                {
  132.                   if( id.ObjectClass == rxclass )
  133.                      yield return (T) tm.GetObject( id, mode, openErased,
  134.                         forceOpenOnLockedLayers );
  135.                }
  136.             }
  137.          }
  138.       }
  139.  
  140.       // Returns true if at least one RXClass is derived from
  141.       // the given RXClass:
  142.  
  143.       public static bool GetHasChildren( this RXClass rxclass )
  144.       {
  145.          foreach( DictionaryEntry e in SystemObjects.ClassDictionary )
  146.          {
  147.             if( rxclass == ( (RXClass) e.Value ).MyParent )
  148.                return true;
  149.          }
  150.          return false;
  151.       }
  152.  
  153.    }
  154. }
  155.    
  156.  
« Last Edit: April 10, 2013, 12:18:38 PM by TT »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Getting all blocks in a drawing based on name.
« Reply #11 on: April 10, 2013, 12:13:17 PM »
Hi,

As you were talking about LINQ, here're two ways using LINQ extension methods.

First, iterating the model space (as said upper, it's not slower than using a selection filter)
Within a transaction, assuming btr is the model space (or any other block table record in which getting block references) and pattern the pattern for the block names to search:
Code - C#: [Select]
  1.             RXClass rxc = RXClass.GetClass(typeof(BlockReference));
  2.             return btr.Cast<ObjectId>()
  3.                 .Where(id => id.ObjectClass == rxc)
  4.                 .Select(id => (BlockReference)id.GetObject(OpenMode.ForRead))
  5.                 .Where(br =>
  6.                     (br.IsDynamicBlock ?
  7.                     ((BlockTableRecord)br.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)).Name :
  8.                     br.Name) == pattern);;

Second, using the BlockTableRecord.GetBlockReferenceIds() as suggested by Will Hatch and some extension methods (as often showned by Tony T and others). This method is really faster when there are not only block references in the model space.

Code - C#: [Select]
  1.     static class Extensions
  2.     {
  3.         public static T GetObject<T>(this ObjectId id) where T : DBObject
  4.         {
  5.             return (T)id.GetObject(OpenMode.ForRead);
  6.         }
  7.  
  8.         public static IEnumerable<T> GetObjects<T>(this SymbolTable source) where T : SymbolTableRecord
  9.         {
  10.             foreach (ObjectId id in source)
  11.                 yield return (T)id.GetObject(OpenMode.ForRead);
  12.         }
  13.  
  14.         public static IEnumerable<BlockReference> GetBlockReferences(this BlockTableRecord btr, ObjectId ownerId)
  15.         {
  16.             return btr.GetStaticReferences(ownerId)
  17.                 .Concat(btr.GetAnonymousBlockIds()
  18.                     .Cast<ObjectId>()
  19.                     .SelectMany(id => id.GetObject<BlockTableRecord>().GetStaticReferences(ownerId)));
  20.         }
  21.  
  22.         public static IEnumerable<BlockReference> GetBlockReferences(this Database db, string pattern, ObjectId ownerId)
  23.         {
  24.             return db.BlockTableId
  25.                 .GetObject<BlockTable>()
  26.                 .GetObjects<BlockTableRecord>()
  27.                 .Where(btr => Autodesk.AutoCAD.Internal.Utils.WcMatch(btr.Name, pattern))
  28.                 .SelectMany(btr => btr.GetBlockReferences(ownerId));
  29.         }
  30.  
  31.         static IEnumerable<BlockReference> GetStaticReferences(this BlockTableRecord btr, ObjectId ownerId)
  32.         {
  33.             return btr.GetBlockReferenceIds(true, false)
  34.                 .Cast<ObjectId>()
  35.                 .Select(id => id.GetObject<BlockReference>())
  36.                 .Where(br => br.OwnerId == ownerId);
  37.         }
  38.     }

Using: from within a transaction call:
Code - C#: [Select]
  1. db.GetBlockReferences(pattern, SymbolUtilityServices.GetBlockModelSpaceId(db))

<EDIT: Tony was faster and his code is more robust...>
« Last Edit: April 10, 2013, 12:18:28 PM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: Getting all blocks in a drawing based on name.
« Reply #12 on: April 10, 2013, 12:36:31 PM »
Quote
<EDIT: Tony was faster and his code is more robust...>

Hi Gile. Very nice use of LINQ.

Where our solutions differ, is that I prefer to use yield return inside of my own LINQ extensions where possible, because within in a single method I can do the work of many Linq methods, and each Linq method adds another iterator to the chain of iterators that produces the final result.

Each iterator in that chain, requires 1 call to IEnumerator<T>.MoveNext() and IEnumerator<T>.get_Current for every element returned.

Hence, something that looks very simple, like this:

   sequence.Where( a=>predicate(a)).Select( a=>b ).SelectMany(b=>b.children)

requires exactly 3 calls to IEnumerator<T>.MoveNext, and exactly 3 calls to IEnumerator<T>.get_Current, for every element returned

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Getting all blocks in a drawing based on name.
« Reply #13 on: April 10, 2013, 12:51:41 PM »
Quote
Hi Gile. Very nice use of LINQ.
Thanks Tony, this is a "natural" way of coding with F# (i was inspired by this post).
In addition to the use of 'yield return', I'd say your code is more robust due to the TopTransaction existence checking.
Speaking English as a French Frog

BlackBox

  • King Gator
  • Posts: 3770
Re: Getting all blocks in a drawing based on name.
« Reply #14 on: April 10, 2013, 01:17:13 PM »
Tony / Gile -

You guys are _way_ over my head with regard to .NET development; I cannot thank you both enough for discussing these topics, and volunteering so many different code samples for others like myself to come back to, and learn from.

Cheers! :beer:
"How we think determines what we do, and what we do determines what we get."