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.D
ocumentManager.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.D
ocumentManager.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
Solved! Go to Solution.
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?
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
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?
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
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!
A little more information... I thought this was working correctly for the last couple of months until I discovered this issue!!!
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?
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