I'm trying to migrate some old macros we have developed to VB.NET and I'm having some difficulties with what (I think) should be a fairly simple thing to do. I'm trying to open a specific block (I know the block name) and read the attributes, but I'm having trouble even getting the block. I've spent about a day now reading up on BlockTableRecords, searching the forums and following different examples, but I can't seem to load any objects in the drawing.
main.vb
Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.ApplicationServices Namespace CADPLUGIN Public Class Commands <CommandMethod("Welcon", CommandFlags.Session)> Public Shared Sub ShowForm() Dim frm As New Form1 'Launch user form Application.ShowModelessDialog(frm) End Sub End Class End Namespace
Form1.vb
Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Public Class Form1 Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click Dim Path As String = "C:\Test\CADPLUGIN\dwgs\11-E-03436_SHT2_rB.dwg" Dim i As Integer 'Set up AutoCAD and load drawing Dim acDocMgr As DocumentCollection = Application.DocumentManager Dim acDoc As Document = DocumentCollectionExtension.Open(acDocMgr, Path, False) Dim acDb As Database = acDocMgr.MdiActiveDocument.Database 'Init transaction manager and get block table Using acTrans As Transaction = acDb.TransactionManager.StartTransaction() Dim acBlkTbl As BlockTable = acTrans.GetObject(acDb.BlockTableId, OpenMode.ForRead) Dim acBlkTblRec As BlockTableRecord = acTrans.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), OpenMode.ForRead) For Each acBlkId As ObjectId In acBlkTblRec 'DO SOMETHING i = i + 1 Next
End Using End Sub End Class
The idea is that I'd loop through all the objects in the Model Space until the block I want is found, and then get it's attributes. I've just set up a simple counter to start off with, which should increment for each object. But when I debug the script it just skips the for loop completely and ends the sub.
What am I doing wrong? How do I access blocks using the ACAD .NET API?
Hi ,
Try to lock the document before starting transaction. As you are using modeless dialog so to access a document you have to lock it first. you can use something like below:
Using acLckDoc AsDocumentLock = acDoc.LockDocument()
Using acTrans AsTransaction = acCurDb.TransactionManager.StartTransaction()
'do as you like
EndUsing
EndUsing
See codes gathered from my old project, change to your suit
Thanks for the replies, but I think I found the problem. I was pointing the database to DocumentManager.MdiActiveDocument.Database
Dim acDocMgr As DocumentCollection = Application.DocumentManager Dim acDoc As Document = DocumentCollectionExtension.Open(acDocMgr, Path, False) Dim acDb As Database = acDocMgr.MdiActiveDocument.Database
Changing it fo acDoc.Database seems to have solved the problem. Here's the final code I came up with. Instead of looping through the entire BlockTableRecord, I'm using a selection filter to create an array of all the blocks in the Model Space and then loop through them. If the block isn't found, it tries again in the Paper Space.
Private Function SelFilter(SearchElem As String, Optional ByVal Space As Integer = 0) 'Create a selection filter to limit the number of iterations required when looping through drawing objects 'Pass the element name as a string and a Model/Paper Space selection '(0=Model Space, 1=Paper Space) Dim acTypValAr(1) As TypedValue acTypValAr.SetValue(New TypedValue(67, Space), 0) acTypValAr.SetValue(New TypedValue(DxfCode.Start, SearchElem), 1) Dim acSelFtr As SelectionFilter = New SelectionFilter(acTypValAr) Return acSelFtr End Function Private Function ReadTitleblock(ByVal dwgPath As String, ByVal Index As Integer) Dim DwgAtts As New TransmittalData Dim bBlkFnd As Boolean = False 'Set up AutoCAD and load drawing Dim acDocMgr As DocumentCollection = Application.DocumentManager Dim acDoc As Document = DocumentCollectionExtension.Open(acDocMgr, dwgPath, True) Dim acDb As Database = acDoc.Database Dim acEd As Editor = acDocMgr.MdiActiveDocument.Editor 'Initialise transaction manager Using acTrans As Transaction = acDb.TransactionManager.StartTransaction() 'Select all blocks in drawing and search for titleblock Dim acObjCol() As ObjectId For i As Integer = 0 To 1 acObjCol = acEd.SelectAll(SelFilter("INSERT", i)).Value.GetObjectIds() For Each acObj As ObjectId In acObjCol Dim acBlkRef As BlockReference = acTrans.GetObject(acObj, OpenMode.ForRead) If acBlkRef.Name.ToUpper() Like gbl.Client(Index).titleblock Then 'Titleblock found, get attributes Dim AcAttCol As AttributeCollection = acBlkRef.AttributeCollection For Each acAtt As ObjectId In AcAttCol Dim acAttRef As AttributeReference = acTrans.GetObject(acAtt, OpenMode.ForRead) 'EVALUATE ATTRIBUTES HERE Next bBlkFnd = True Exit For End If Next 'Titleblock attributes found 'Do not attempt Paper Space search If bBlkFnd = True Then Exit For End If Next End Using 'Close drawing DocumentExtension.CloseAndDiscard(acDoc) 'Return drawing attributes Return DwgAtts End Function
This seems to work fine. The function is returning a custom structure I've made that contains all the attribute values that I need. However now I'm having problems closing the documents. AutoCAD crashes after calling the function. If I remove the DocumentExtension.CloseAndDiscard(acDoc) line it is fine, except the documents obviously stay opened.
After some more debugging and investigation I have worked out the cause of the original problem. It seems that MdiActiveDocument is not updating when I load a new document.
Dim acDocMgr As DocumentCollection = Application.DocumentManager Dim acDoc As Document = DocumentCollectionExtension.Open(acDocMgr, dwgPath, True)
When I load the new drawing using DocumentcollectionExtension.Open, acDogMgr.MdiActiveDocument is not updating to the newly opened document. That is why when I set the Database to acDocMgr.MdiActiveDocument.Database it wasn't finding any elements in the BlockTableRecord (it was loading the BlockTableRecord for the blank document that AutoCAD loads upon opening).
I'm now trying to close the document with:
DocumentExtension.CloseAndDiscard(acDocMgr.MdiActiveDocument)
But obviously this isn't working because MdiActiveDocument isn't actually pointing to the open document. What is going on?
Can you add below line after opening your drawing? As you did not make it active so you should not expect that it become active design automatically. Please add the below line and try once again
acDocMgr.MdiActiveDocument = acDoc