I'm working on a program that cycles through each paperspace layout in a drawing and updates the sheet number and total quantity of sheets in the titleblock attributes. I've updated the block attributes in paperspace before, and it worked fine. Now that I'm trying to cycle through each layout, I cannot get it to work. I have attached my code. I beleive it starts to cycle through the layouts, but it fails when I try to grab the attribute collection. I would greatly appreciate any suggestions.
Public Sub UpdateSheetNumbers() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim layoutCount As Integer = LayoutManager.Current.LayoutCount - 1 Dim trans As Transaction = db.TransactionManager.StartTransaction() Try Dim myBT As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead) For Each btrid As ObjectId In myBT Dim myBTR As BlockTableRecord = btrid.GetObject(OpenMode.ForRead) If myBTR.IsLayout Then Dim layOut As Layout = myBTR.LayoutId.GetObject(OpenMode.ForRead) If layOut.LayoutName = "Model" Then Continue For ''skip remaining code, go to next iteration End If For Each id As ObjectId In myBTR Dim acBref As BlockReference = id.GetObject(OpenMode.ForWrite) If acBref.AttributeCollection.Count <> 0 Then MsgBox(layOut.LayoutName) Dim myAttColl As AttributeCollection = acBref.AttributeCollection For Each myAttRefId2 As ObjectId In myAttColl Dim myAttRef As AttributeReference = myAttRefId2.GetObject(OpenMode.ForWrite) If myAttRef.Tag = "SHT" Then myAttRef.TextString = layOut.TabOrder.ToString() If myAttRef.Tag = "OF" Then myAttRef.TextString = layoutCount.ToString() Next End If Next End If Next Catch ed.WriteMessage("Error!") Finally trans.Commit() End Try End Sub
Well, it fails because your code is build on wrong concept:
For Each id As ObjectId In myBTR
Dim acBref As BlockReference = id.GetObject(OpenMode.ForWrite)
If acBref.AttributeCollection.Count <> 0 Then
...
myBTR is an BlockTableRecord (block definotion). Why do you loop through it to find a BlockReference of itself? Your next line of code may well return you a null (Nothing) object, because entities in a Block definition is very likely not a BlockReference, thus the error when you try to get to AttributeCollection in next line of code.
You should get all BlockReference of the myBRT by:
Dim brefIDs As ObjectIdCollection=myBRT.GetBlockReferenceIds()
Then you can open each BlockReference to see which layout it is on, then you can update its attribute accordingly.
As matter of fact, you do not even bother with BlockTableRecord at all, if you know the block name:
1. Use SelectionSet with filter (Block Name) to select all BlockReferences of the targeting title block.
2. For each BlockReference, check which layer it is on
3. Update attribute accordingly
Norman Yuan
Here is my 2
Public Sub UpdateSheetNumbers() Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Try Using tr As Transaction = db.TransactionManager.StartTransaction ' Open dictionary for reading Dim layoutDict As DBDictionary = DirectCast(tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead), DBDictionary) ' Loop through dictionary entries For Each entry As DictionaryEntry In layoutDict Dim ltr As Layout = DirectCast(tr.GetObject(DirectCast(entry.Value, ObjectId), OpenMode.ForRead), Layout) If ltr.LayoutName = "Model" Then Continue For ''skip remaining code, go to next iteration End If If Not ltr.IsWriteEnabled Then ltr.UpgradeOpen() ed.WriteMessage(vbLf & "{0}" & vbTab & "{1}", ltr.TabOrder, ltr.LayoutName) Dim btr As BlockTableRecord = DirectCast(tr.GetObject(ltr.BlockTableRecordId, OpenMode.ForRead), BlockTableRecord) For Each id As ObjectId In btr Dim ent As Entity = DirectCast(tr.GetObject(id, OpenMode.ForWrite), Entity) Dim acBref As BlockReference = TryCast(ent, BlockReference) If acBref IsNot Nothing Then Dim titlebtr As BlockTableRecord = DirectCast(tr.GetObject(acBref.BlockTableRecord, OpenMode.ForRead), BlockTableRecord) If titlebtr.Name = "TITLEBLOCK" Then If acBref.AttributeCollection.Count <> 0 Then Dim myAttColl As AttributeCollection = acBref.AttributeCollection For Each myAttRefId2 As ObjectId In myAttColl Dim myAttRef As AttributeReference = DirectCast(tr.GetObject(myAttRefId2, OpenMode.ForWrite), AttributeReference) Select Case myAttRef.Tag Case "SHT" : myAttRef.TextString = ltr.TabOrder.ToString() Case "OF" : myAttRef.TextString = ltr.LayoutName End Select Next End If End If End If Next Next tr.Commit() End Using ed.Regen() Catch ex As Autodesk.AutoCAD.Runtime.Exception Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.ToString & vbCr & ex.Message) End Try End Sub
~'J'~
Thanks for the input.
Hallex,
I tried using your code, but I can't get it to work. It appears to fail at:
Dim ent As Entity = DirectCast(tr.GetObject(id, OpenMode.ForWrite), Entity)
also it fails at:
If Not ltr.IsWriteEnabled Then ltr.UpgradeOpen()
I'm curious why you are casting the objects in the block table record to an entity, then retrieveing the block reference from that.
I modified the code using getblockreferenceids, then retrieved the blockreference, but that failed also.
Any suggestions would be greatly appreciated.
thanks
btm
I tested code on A2009, perhaps on your Acad release it
could not work
Just a guess, try instead:
Dim ent As DBObject = DirectCast(tr.GetObject(id, OpenMode.ForWrite, False), DBObject)
But, you didn't answer me on my prior response here, I don't like this manner:
http://forums.autodesk.com/t5/NET/Creating-a-new-layout-from-a-template/td-p/3520808
~'J'~
Thanks again for the fast feedback.
I tried changing Enitity to dbobject, but it still throws an exception at that line.
I'm using 2013, but it should still work. I'm going to keep experimenting with that area of the program to see if I can come up with something that works.
I'll get back to you on the other thread dealing with inserting new layouts. I'm trying to get this attribute issue fixed first.
Thanks
btm
Well, I've been playing with this code for hours, but can't seem to get it to work with 2013.
My latest code is posted below.
It fails at:
Dim AttRef AsAttributeReference = AttRefID.GetObject(OpenMode.ForWrite)
I also commented where it fails in the code below. I'm puzzled at this point. I've edited block attributes several times with VB.net and never had an issue. I'm probably overlooking something simple. If someone can take a look and make any suggestions, I would greatly appreciate it.
Thanks
btm
Public Sub MAIN() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim layoutCount As Integer = LayoutManager.Current.LayoutCount - 1 Dim trans As Transaction = db.TransactionManager.StartTransaction() Try Dim myBT As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead) For Each btrid As ObjectId In myBT Dim myBTR As BlockTableRecord = btrid.GetObject(OpenMode.ForRead) If myBTR.IsLayout Then Dim layOut As Layout = myBTR.LayoutId.GetObject(OpenMode.ForRead) If layOut.LayoutName = "Model" Then Continue For End If For Each id As ObjectId In myBTR Dim obj As DBObject = id.GetObject(OpenMode.ForRead) If TypeOf obj Is BlockReference Then Dim bref As BlockReference = DirectCast(obj, BlockReference) If bref.AttributeCollection.Count <> 0 Then Dim AttColl As AttributeCollection = bref.AttributeCollection For Each AttRefID As ObjectId In AttColl Dim AttRef As AttributeReference = AttRefID.GetObject(OpenMode.ForWrite) ''fails here If AttRef.Tag = "OF" Then AttRef.TextString = layoutCount.ToString() If AttRef.Tag = "SHT" Then AttRef.TextString = layOut.TabOrder.ToString() Next End If End If Next End If Next Catch ed.WriteMessage("Error!") Finally trans.Commit() End Try End Sub
I finally found my problem. I forgot to use lockdocument.
After adding that, everything worked fine in the code I copied into my last post.
Thanks to those that offered some suggestions.
btm