• Industries
  • Products
  • Buy
  • Services & Support
  • Communities
  • Discussion Groups

    .NET

    Reply
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007
    Accepted Solution

    All blocks by name

    460 Views, 11 Replies
    11-17-2011 02:47 PM

    Hi All,

     

    I know I am missing something very simple here (and i am sure I have done this before, but cannot find the code), but I basically need to get all blocks (dynamic in this case) with a particular name.

     

    I can do it easily by searching through all blocks in the drawing, but it's a bit slow:

     

    Dim acDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
    'Declare document lock
    Dim acDocLock As DocumentLock = acDoc.LockDocument
    Dim acCurDb As Database = acDoc.Database
    
            'Start a transaction to open the recently copied title-block file and edit the attributes
            Using acTrans As Transaction = acDoc.TransactionManager.StartTransaction
                Try
    
                    'Get the blocktable from the current open drawing
                    Dim acBT As BlockTable = acCurDb.BlockTableId.GetObject(OpenMode.ForRead)
                    
                    'Iterrate through block table to determine if they are layouts or not
                    For Each btrObjId As ObjectId In acBT
    
                        Dim btr As BlockTableRecord = btrObjId.GetObject(OpenMode.ForRead)
    
                        If btr.IsLayout Then
                            Continue For
                        End If
    
                        Dim objIdColl As ObjectIdCollection = btr.GetBlockReferenceIds(True, True)
    
                        For Each objId In objIdColl
    
                            Dim acBref As BlockReference = objId.GetObject(OpenMode.ForWrite)
                            Dim myAttValsTB As New Dictionary(Of String, String)
    
                            If acBref.Name = "ECDT_IssueStatus" Then
                               
    			'Do stuff to attributes here...
    
                            End If
    
                            If acBref.IsDynamicBlock = True Then
                                Dim acBlkDef As BlockTableRecord = acBref.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)
                                Dim acDynObjId As ObjectId = acBref.DynamicBlockTableRecord
                                Dim dynBtr As BlockTableRecord = CType(acTrans.GetObject(acDynObjId, OpenMode.ForRead, False), BlockTableRecord)
                                If dynBtr.Name = "ECDT_IssueStatus" Then
    
    		  	    'Do stuff to dynamic properties here...
    
                                    Dim strDynParameterName As String = "Visibility1"
                                    Dim strDynValue As String = strSubState
                                    Dim acDynBref As BlockReference = objId.GetObject(OpenMode.ForRead)
                                    For Each myDynamProp As DynamicBlockReferenceProperty In _
                                        acDynBref.DynamicBlockReferencePropertyCollection()
                                        If myDynamProp.PropertyName.Equals( _
                                            strDynParameterName, StringComparison.OrdinalIgnoreCase) = True Then
                                            myDynamProp.Value = strDynValue
                                        End If
                                    Next
                                End If
                            End If
    
                        Next
                    Next
    Finally
                End Try
    	actrans.commit()
    acDocLock.Dispose()
    
            End Using

     

     Again, the above works with no problems other than being slow.

     

    So I figured since I already know the block name, I can just use it to get all of the blocks in the drawing.  But for some reason, with the code below, the collection (btrids) is never populated.  What am I doing wrong?

     

    Dim Blockname As String = "ECDT_IssueStatus"
    Dim acDoc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
    'Declare document lock
    Dim acDocLock As DocumentLock = acDoc.LockDocument
    Dim acCurDb As Database = acDoc.Database
    
            Using acTrans As Transaction = acDoc.TransactionManager.StartTransaction
                Try
    
                    Dim BT As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
                   
                    If BT.Has(Blockname) Then
                        Dim btr As BlockTableRecord = acTrans.GetObject(bt(Blockname), OpenMode.ForRead)
                        
                        Dim btrIDs As ObjectIdCollection = btr.GetBlockReferenceIds(True, False)
                      
                        For Each id As ObjectId In btrIDs
                            If Not id.IsEffectivelyErased Then
    
                                Dim acBref As BlockReference = id.GetObject(OpenMode.ForWrite)
                                
    				'Do stuff to block attributes here...
                                
                                If acBref.IsDynamicBlock = True Then
                                    Dim acBlkDef As BlockTableRecord = acBref.DynamicBlockTableRecord.GetObject(OpenMode.ForRead)
                                    Dim acDynObjId As ObjectId = acBref.DynamicBlockTableRecord
                                    Dim dynBtr As BlockTableRecord = CType(acTrans.GetObject(acDynObjId, OpenMode.ForRead, False), BlockTableRecord)
                                    If dynBtr.Name = "ECDT_IssueStatus" Then
    
                                       'Do stuff to dynamic properties here...
    
                                    End If
                                End If
    
                            End If
                        Next
                    End If
    
                    acTrans.Commit()
                    acDocLock.Dispose()
    
               Finally
                End Try
            End Using

     

     

    Please use plain text.
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007

    Re: All blocks by name

    11-17-2011 03:37 PM in reply to: Dull_Blades

    OK... tested this with a normal block, so it has something to do with the fact that it is a dynamic block.  So how can I get all of the dynamic block references in the drawing using the blockname?

     

    Please use plain text.
    Mentor
    Posts: 241
    Registered: ‎05-12-2009

    Re: All blocks by name

    11-17-2011 03:46 PM in reply to: Dull_Blades

    When you insert a dynamic block and change a dynamic propertty then a anonymous block is created.

     

    BlockTableRecord.GetBlockReferenceIds should only contain the blockreferences that have not had any changes to dynamic properties(default state)

     

     

    You can use BlockTableRecord.GetAnonymousBlockIds to get anonymous block ids.

     

    Also you can use BlockTableRecord.IsDynamicBlock Property to check at the definition level.

     

    Here is a little code to help explain

     

    <CommandMethod("ChangeVisState")> _
            Public Sub ChangeVisState()
                Dim Blockname As String = "C"
    
                Dim doc As Document = Application.DocumentManager.MdiActiveDocument
                Dim db As Database = doc.Database
                Dim ed As Editor = doc.Editor
    
    
                Using trx As Transaction = db.TransactionManager.StartTransaction
    
    
                    Dim bt As BlockTable = trx.GetObject(db.BlockTableId, OpenMode.ForRead)
    
                    If bt.Has(Blockname) Then
    
                        Dim btr As BlockTableRecord = trx.GetObject(bt(Blockname), OpenMode.ForRead)
    
                        Dim btrIDs As ObjectIdCollection = btr.GetBlockReferenceIds(True, False)
    
                        If btr.IsDynamicBlock Then
    
                            For Each id As ObjectId In btr.GetAnonymousBlockIds
                                Dim anonBlock As BlockTableRecord = trx.GetObject(id, OpenMode.ForRead)
                                For Each anonId As ObjectId In anonBlock.GetBlockReferenceIds(True, False)
                                    btrIDs.Add(anonId)
                                Next
                            Next
    
                        End If
    
                        For Each id As ObjectId In btrIDs
                            If Not id.IsEffectivelyErased Then
    
                                Dim bref As BlockReference = id.GetObject(OpenMode.ForWrite)
    
                                For Each dynProp As DynamicBlockReferenceProperty In bref.DynamicBlockReferencePropertyCollection
                                    If dynProp.PropertyName = "Visibility1" Then
                                        dynProp.Value = "VisibilityState3"
                                    End If
                                Next
    
                            End If
                        Next
                    End If
    
                    trx.Commit()
    
    
                End Using
            End Sub

     

    You can also find your answers @ TheSwamp
    Please use plain text.
    Distinguished Contributor
    Posts: 181
    Registered: ‎09-22-2006

    Re: All blocks by name

    11-17-2011 04:04 PM in reply to: Dull_Blades

    This is C# but might help.

    http://www.theswamp.org/index.php?topic=31859.msg422435#msg422435

     

    CAD Programming Solutions
    Please use plain text.
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007

    Re: All blocks by name

    11-18-2011 08:51 AM in reply to: mohnston

    Excellent!  Works great and good reading!!!

     

    I have been able to work with dynamic blocks in the past with no isses, but only after processing them with a user pick. Never tried to do it by itterating the block table so I was not sure how to do it (with a name).

     

    Taking this a step further... how can I filter this down to only pulling the dynamic blocks (by a certain name) from the current layout?

     

    I use the following for a standard block reference and then filter for the name later, but not sure how in this case:

     

    acBtr = acCurDb.CurrentSpaceId.GetObject(OpenMode.ForRead)

     How do I filter that with a dynamic block name?

    Please use plain text.
    Mentor
    Posts: 241
    Registered: ‎05-12-2009

    Re: All blocks by name

    11-18-2011 09:05 AM in reply to: Dull_Blades

    You could itterate the space and make sure it's BlockReference.DynamicBlockTableRecord property match or checking a blockreference's BlockId to see if it matches Database.CurrentSpaceId

    You can also find your answers @ TheSwamp
    Please use plain text.
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007

    Re: All blocks by name

    11-18-2011 09:21 AM in reply to: jeff

    Got it!  Thanks for the tip!

    Please use plain text.
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007

    Re: All blocks by name

    01-17-2012 01:13 PM in reply to: Dull_Blades

    Now that I have all of the blocks in the drawing by particular name, I need to keep only those that are currently inserted in ModelSpace. 

     

    Can someone kindly give me some advice on doing this?  I am struggling with it a bit!

     

    TIA

    Please use plain text.
    Mentor
    Dull_Blades
    Posts: 231
    Registered: ‎06-25-2007

    Re: All blocks by name

    01-17-2012 03:03 PM in reply to: Dull_Blades

    A little more information... I thought this was working correctly for the last couple of months until I discovered this issue!!! :smileyembarrassed:

     

    The problem I am running into is that the code reads the entire database.  It even searches within other blocks!  So for example, if I code to get all blocks with the name of "ABLOCK" it searches the entire database and adds every instance of that block to my collection. The real problem is that it will search for instances of "ABLOCK" nested in a block that has a block table record, but is not inserted in the drawing.

     

    So in other words, if I create a new block called "BBLOCK" that contains a nested "ABLOCK", then I delete the "BBLOCK" but do not purge the drawing, the code will still count the "ABLOCK".  I have coded for .IsErased and IsEffectivelyErased, but I think this is something different.

     

    So... how can I get all blocks named "ABLOCK" by name that are BlockReferences (Inserts) in ModelSpace only?  And, not as important, but how do I stop it from searching for nested blocks?

     

    The code I am using is below:

     

    Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction
    
                    Try
                    Dim acBt As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
                    
                    If acBt.Has(blkName) Then
    
                        Dim acBtr As BlockTableRecord = acTrans.GetObject(acBt(blkName), OpenMode.ForRead)
                        Dim objIdColl As ObjectIdCollection = acBtr.GetBlockReferenceIds(True, False)
    
                        If acBtr.IsDynamicBlock Then
                            For Each id As ObjectId In acBtr.GetAnonymousBlockIds
                                If Not id.IsEffectivelyErased Then
                                    If Not id.IsErased Then
                                        Dim anonBlock As BlockTableRecord = acTrans.GetObject(id, OpenMode.ForRead)
                                        For Each anonId As ObjectId In anonBlock.GetBlockReferenceIds(True, False)
                                            objIdColl.Add(anonId)
                                        Next
                                    End If
                                End If
                            Next
                        End If
    
                        For Each objId As ObjectId In objIdColl
                            Dim acEnt As Entity = objId.GetObject(OpenMode.ForRead) 'added
                            If Not objId.IsEffectivelyErased Then
                                If Not objId.IsErased Then
    
                                    Dim acBref As BlockReference = objId.GetObject(OpenMode.ForRead)
    
    '<Snip>

     

    Maybe I should start a new thread on this "sub" topic as this thread is a couple of months old?

     

     

    Please use plain text.
    Mentor
    Posts: 241
    Registered: ‎05-12-2009

    Re: All blocks by name

    01-17-2012 06:05 PM in reply to: Dull_Blades

    Have not tested, but

     

    For both cases of counting inserts with the block nested or not.

    To not count the instance's inserted in other definitions you could check if the blockreference's OwnerId or BlockId is equal to ModelSpace's ObjectId

     

    So to get the refereneces in model space that are nested set the first argument of GetBlockReferenceIds to false or true if not wanting the nested references, but for both cases excluded the blockreference's where OwnerId or BlockId do not match a modelspace or any of the spaces Objectid

    You can also find your answers @ TheSwamp
    Please use plain text.