Hello, this is the code that I've put together by reading posts on here and elsewhere.
I have drawings in a library folder like "SMD_0201.dwg". The drawing consists of a rectangle and an invisible attribute.
I want to bring the appropriate block (dwg) into my drawing and place the references at the correct location and rotation with the correct attribute text.
When I use this, all seems to run fine (the rectangles show up placed perfectly and rotated), but there are no attributes. Then when I double pick on the block, it does not have a block name, it doesn't even have a definition.
BlockName = the path and name of the block
basename = block name
Public Sub InsertSMDComp(ByVal InsPt As Point3d, ByVal BlockName As String, ByVal basename As String, ByVal RefDes As String, _ ByVal rotation As Double, ByVal scale As Double) Dim smdTransMan As DatabaseServices.TransactionManager Dim smdTrans As DatabaseServices.Transaction Dim myDwg As Document Dim myBT As BlockTable myDwg = Application.DocumentManager.MdiActiveDocument smdTransMan = myDwg.TransactionManager smdTrans = smdTransMan.StartTransaction myBT = myDwg.Database.BlockTableId.GetObject(OpenMode.For
Read) Dim myDB As Database myDB = myDwg.Database Try Using db As Database = New Database(False, True) db.ReadDwgFile(BlockName, IO.FileShare.Read, True, "") Dim BlkId As ObjectId BlkId = myDB.Insert(BlockName, db, True) Dim bt As BlockTable = smdTrans.GetObject(myDB.BlockTableId, OpenMode.ForRead, True) Dim btr As BlockTableRecord = smdTrans.GetObject(bt(BlockTableRecord.ModelSpace) , OpenMode.ForWrite, True) Dim bref As BlockReference bref = New BlockReference(InsPt, BlkId) bref.Rotation = rotation bref.ScaleFactors = New Scale3d(scale, scale, scale) btr.AppendEntity(bref) smdTrans.AddNewlyCreatedDBObject(bref, True) 'Set the Attribute Value Dim myAttColl As DatabaseServices.AttributeCollection Dim myEnt As DatabaseServices.Entity Dim myBTREnum As BlockTableRecordEnumerator myAttColl = bref.AttributeCollection myBTREnum = btr.GetEnumerator While myBTREnum.MoveNext myEnt = myBTREnum.Current.GetObject(OpenMode.ForWrite) If TypeOf myEnt Is DatabaseServices.AttributeDefinition Then Dim myAttDef As DatabaseServices.AttributeDefinition = myEnt Dim myAttRef As New DatabaseServices.AttributeReference myAttRef.SetAttributeFromBlock(myAttDef, bref.BlockTransform) myAttRef.TextString = RefDes myAttColl.AppendAttribute(myAttRef) smdTrans.AddNewlyCreatedDBObject(myAttRef, True) End If End While smdTrans.Commit() End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception MsgBox(ex.ToString) End Try smdTrans.Dispose() smdTransMan.Dispose() End Sub
Solved! Go to Solution.
The problem that you get a block definition without name created in the drawing, is because the block definition was inserted into this drawing with an INVALID block name. The offending code is:
BlkId = myDB.Insert(BlockName, db, True)
necause BlockName is a file name with a full folder path, which includes characters line ":","\" that are not allowed to be used in block name. When you manulally define block, AutoCAD validates the block name; while you inserts block with .NET API's Database.Insert(), if the block name contaims invalid characters, the block definition can still be inserted, the AutoCAD somehow gives the block definition an empty name, instead of raising an exception. I personally think this is an API bug.
Based on your sub's signature, I think you meant to use "baseName" as block name in the Insert() method.
However, you should follow AutoCAD convention to use block's file name as Block name, in general. So, you do not need to pass "baseName" to the subroutine.
you can get block file name from its full path name like this:
Dim bName As String=System.IO.Path.GetFileNameWothoutExtension(
Thanks, but when I try to use you suggestion, it tells me "GetFileNameWothoutExtension is not a member of 'System.IO.Path"
Above the sub, I do have:
Also, if I don't include the path, how does it know where to get the drawing?
Never mind, I got it.
Since I already have the "basename" I used that instead.
Using db AsDatabase = NewDatabase(False, True)
db.ReadDwgFile(BlockName, IO.FileShare.Read, True, "")
'Dim bName As String = System.IO.Path.GetFileNameWothoutExtension(BlockNa
Dim BlkId AsObjectId
BlkId = myDB.Insert(basename, db, True)
Ok, was too quick on that. The blocks come in, and they look good. They even have names and definitions (including an attribute definition) in the block editor. The only problem now is how to insert them and apply a text value to the attribute?
I thought this should work. it includes the value for the attribute textstring.
While myBTREnum.MoveNext myEnt = myBTREnum.Current.GetObject(OpenMode.ForWrite) If TypeOf myEnt Is DatabaseServices.AttributeDefinition Then Dim myAttDef As DatabaseServices.AttributeDefinition = myEnt Dim myAttRef As New DatabaseServices.AttributeReference myAttRef.SetAttributeFromBlock(myAttDef, bref.BlockTransform) myAttRef.TextString = RefDes myAttColl.AppendAttribute(myAttRef) smdTrans.AddNewlyCreatedDBObject(myAttRef, True) End If End While
Also, when I try to do a manual insert on one of these existing blocks, there is a problem.
When I pick a name from the list, and pick ok to insert, it pops up with "The specified file was not found. Specify the correct filename or select a valid block name."
If you had stepped through your code in debugging, you would have easily found out thta the code inside the "If...End If"
If TypeOf myEnt Is... Then
is never reached, thus, no attribute reference is added to the BlockReference. Do not just run code and wonder why it is not work. Debugging teaches a lot.
The problem is with Enumerator "myBTREnum", which you declare as BlockTableEnumerator. Why you need to iterate through BlockTable for an AttributeDefinition that belongs to a BlockTablerecord? You'll never find an AttributeDefinition in BlockTable.
Instead, what you need is to interate through the BlockTableRecord your BlockReference is based on to find AttributeDefinition. The code would be like:
''btr is the BlockTableRecord
For Each id As ObjectId in btr
If TypeOf myEnt Is AttributeDefinition Then
Dim myAttDef As AttributeDefinition = myEnt
''Create attribute reference based on the attributeDefinition
Dim attrRef As New Attributereference
Debugging, and Debugging. If you step thorugh the code, or place a break point at where InsertSMDComp() is called, you simply examine what the block name and block file path is passed to this subroutine, then you probably can tell why you get that error message.
If the code passes a file name without full path, the .NET runtime would only examine the current application folder for the block file. Thta is probably why the message. You need either supply full path, or write code to search through AutoCAd supporting folders to find block file by only given file name.
Hi Norman, you, as always, have been very helpful. I did run through the code many, many times. I put in the breaks, even inside the If...End If block and it stopped there. I had watches going as well. That's why this was so confusing and frustrating.
The blocks in my library include a rectangle and a block attribute, but sometimes it sounds like I'm having to add a new attribute to the block that just got read in.
I had the code you mentioned above in regards to btr and bref:
Dim btr As BlockTableRecord = smdTrans.GetObject(bt(BlockTableRecord.ModelSpace)
, OpenMode.ForWrite, True) Dim bref As BlockReference bref = New BlockReference(InsPt, BlkId) bref.Rotation = rotation bref.ScaleFactors = New Scale3d(scale, scale, scale) btr.AppendEntity(bref)
Since bref was included in the If..End If, I thought it was using the attribute in the block.
When I look up some of these things, like enumerator, most of the help I find is really no help. All of the writers think their audience must be full time programmers with multiple degrees. I'm not one to just splice code together and cross my fingers hoping it will work. I search the Internet trying to understand what it is.
Thanks for all the time you've already spent helping me. I really do appreciate it.