Author Topic: Create new BlockTableRecord, containing several others  (Read 7387 times)

0 Members and 1 Guest are viewing this topic.

Bert

  • Guest
Create new BlockTableRecord, containing several others
« on: October 29, 2013, 06:20:19 AM »
Hello guys,

I'm really puzzeled why I haven't been able to do this yet (VB.NET). ..

Goal is to have a Blockreference in ModelSpace that is composed/holds several Blocks in it.

So I start off with having al the 'component-blocks' as BlockTableRecords in my BlockTable.
What I need to do is create a new BlockTableRecord, add the desired BlockTableRecords IN the new one (incrementing thier location so there is no overlap of blocks/entities)
then just create a BlockReference and insert it in ModelSpace.

I've done alot with BlockTableRecords, ObjectId's, Entities and BlockReferences; But i'm not getting this working ..

Can you kindly shove me into the right direction  please ?



Jeff H

  • Needs a day job
  • Posts: 6150
Re: Create new BlockTableRecord, containing several others
« Reply #1 on: October 29, 2013, 07:17:44 AM »
Create a new BlockTableRecord then add a BlockReference for each of the Blocks you want it to have?

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #2 on: October 29, 2013, 08:01:29 AM »
Can I append a BlockReference to a BlockTableRecord as so?

Code: [Select]
acBlkTblRec.AppendEntity(acBlkRef)
acTrans.AddNewlyCreatedDBObject(acBlkRef, True)

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #3 on: October 29, 2013, 09:04:01 AM »
What i've done is the following :

Code - vb.net: [Select]
  1.    
  2. Shared Sub addDetailstoBlock(_detailBlocksArray As List(Of String))
  3.         ' Get the current document and database
  4.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  5.         Dim acCurDb As Database = acDoc.Database
  6.         ' Lock the document
  7.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  8.             ' Start a transaction in the database
  9.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  10.                 ' Get the Block table for the current database
  11.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  12.                 ' Get the ModelSpace
  13.                 Dim modelSpace As BlockTableRecord = DirectCast(acTrans.GetObject(acBlockTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
  14.                 ' Get a fresh Name for our new DetailBlock (getDetailBlockSeqNum retrieves a Sequence number)
  15.                 Dim blkName As String = "DetailBlock_" & getDetailBlockSeqNum()
  16.                 ' Create the BlockTableRecord
  17.                 Dim acBlkTblRecId As ObjectId = Acad.findOrCreateBlockTableRecord(blkName).ObjectId
  18.                 ' Get the newly created BlockTableRecord
  19.                 Dim acBlkTblRec As BlockTableRecord = DirectCast(acTrans.GetObject(acBlkTblRecId, OpenMode.ForWrite), BlockTableRecord)
  20.  
  21.                 'Declare a insertionPoint for inserting the 'sub-blocks'
  22.                 Dim subBlkInsPnt As New Point3d(0, 0, 0)
  23.                 'Traverse the _detailBlocksArray wich holds all desired 'sub-blocks'-names
  24.                 For Each subBlockName As String In _detailBlocksArray
  25.                     'searchOrImportBlock checks if the desired 'sub-block' exists in Block table current database, if not it imports it from a librairy-DWG
  26.                     'either way, this gives us a ObjectId of the desired 'sub-block'
  27.                     Dim subBlkId As ObjectId = Acad.searchOrImportBlock(Acad.MontageDetails, subBlockName)
  28.                     'Create a BlockReference using the subBlkInsPnt & subBlkId
  29.                     Dim subBlkRef As BlockReference = New BlockReference(subBlkInsPnt, subBlkId)
  30.                     'Add the subBlkRef to our 'main' BlockTableRecord
  31.                     acBlkTblRec.AppendEntity(subBlkRef)
  32.                     acTrans.AddNewlyCreatedDBObject(subBlkRef, True)
  33.                     'Increment the X-value of our subBlkInsPnt
  34.                     Dim blkEx As Extents3d = subBlkRef.GeometricExtents()
  35.                     subBlkInsPnt = New Point3d(subBlkInsPnt.X + Math.Abs(blkEx.MinPoint.X - blkEx.MaxPoint.X), subBlkInsPnt.Y, 0)
  36.                 Next
  37.  
  38.                 'Determine a insertionPoint for our 'main' BlockReference
  39.                 insPnt = New Point3d(-15000, 15000, 0)
  40.  
  41.                 'Create and add our 'main' BlockReference to the ModelSpace
  42.                 Dim acBlkRef As New BlockReference(insPnt, acBlkTblRecId)
  43.                 modelSpace.AppendEntity(acBlkRef)
  44.                 acTrans.AddNewlyCreatedDBObject(acBlkRef, True)
  45.  
  46.                 ' Commit the transaction
  47.                 acTrans.Commit()
  48.             End Using ' Dispose of the transaction
  49.         End Using ' Unlock the document
  50.     End Sub
  51.  

Problem is that this initially works (the code runs) but it results in a dodgy BlockReference.
AutoCAD crashes as soon as I select or even mouse-over it in Modelspace :
Command: ** Undefined block #-10024224

Why would it do this ?
« Last Edit: October 30, 2013, 04:14:11 AM by Bert »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Create new BlockTableRecord, containing several others
« Reply #4 on: October 29, 2013, 09:53:56 AM »
It is hard to tell what you are doing looking at code and are adding these to modelspace or another BlockTableRecord.
 
 
Something to keep in mind is it usually a sign a that a method is doing too much when its name has 2 verbs in it.
 
A computer can not read C# or VB.NET or any other high level language until compiled, So the only people who can read it are people.
 
I probably will have some completely disagree, but try to let your code explain what your doing and not use comments.
Maybe comments at top of class to give a high level overview.
 
Using SOLID, method naming, you can usually layout you code where reading a method you can tell what it does and if interested you can go to the methods implementation called inside method to see how its done, its name should tell you what its does.
 
 

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #5 on: October 29, 2013, 10:12:58 AM »
Agreed. Totally.
It's an ongoing battle of mine to refactor my code.
Altough, in my defense, the code I posted was heavely commented for the purpose of posting it. More so then I'd normally would !

The 2 methods i use in the above sample are as follows :
(but I insure you, independently these have proven to work well in the past)
Code - vb.net: [Select]
  1.  
  2. Shared Function findOrCreateBlockTableRecord(_blockName As String) As BlockTableRecord
  3.  
  4.         ' Get the current document and database, and start a transaction
  5.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  6.         Dim acCurDb As Database = acDoc.Database
  7.         ' Lock the document
  8.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  9.             ' Start a transaction in the database
  10.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  11.                 ' Get the Block table for the current database
  12.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  13.  
  14.                 ' If found in local BlockTableRecord
  15.                 If acBlockTbl.Has(_blockName) Then
  16.                     Dim rtnBlock As BlockTableRecord
  17.                     rtnBlock = acTrans.GetObject(acBlockTbl(_blockName), OpenMode.ForWrite)
  18.                     Return rtnBlock
  19.                 Else
  20.                     ' Create our new block table record...
  21.                     Dim rtnBlock As New BlockTableRecord
  22.                     ' ... and set its properties
  23.                     rtnBlock.Name = _blockName
  24.                     ' Add the new block to the block table
  25.                     acBlockTbl.UpgradeOpen()
  26.  
  27.                     Dim btrId As ObjectId = acBlockTbl.Add(rtnBlock)
  28.                     acTrans.AddNewlyCreatedDBObject(rtnBlock, True)
  29.                     ' Commit the transaction
  30.                     acTrans.Commit()
  31.                     Return rtnBlock
  32.                 End If
  33.  
  34.             End Using ' Dispose of the transaction
  35.         End Using ' Unlock the document
  36.  
  37.     End Function
  38.  

Code - vb.net: [Select]
  1.    
  2. Shared Function searchOrImportBlock(_sourceFileName As String, _blockName As String) As ObjectId
  3.         ' Get the current document and database
  4.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  5.         Dim acCurDb As Database = acDoc.Database
  6.         ' Lock the document
  7.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  8.             ' Start a transaction in the database
  9.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  10.                 ' Get the Block table for the current database
  11.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  12.  
  13.                 ' If found in local BlockTableRecord
  14.                 If acBlockTbl.Has(_blockName) Then
  15.                     Return acBlockTbl(_blockName)
  16.                 End If
  17.  
  18.             End Using ' Dispose of the transaction
  19.         End Using ' Unlock the document
  20.  
  21.         ' If not found in local BlockTableRecord then Import from _sourceFileName
  22.         Return importBlockFromSourceFile(_sourceFileName, _blockName)
  23.  
  24.     End Function
  25.  

Code - vb.net: [Select]
  1. Shared Function importBlockFromSourceFile(_sourceFileName As String, _blockName As String) As ObjectId
  2.  
  3.         Dim sourceDb As New Database(False, True)
  4.         Dim acObjIdColl As ObjectIdCollection = New ObjectIdCollection()
  5.  
  6.         ' Read the DWG into a side database
  7.         sourceDb.ReadDwgFile(_sourceFileName, System.IO.FileShare.Read, True, "")
  8.  
  9.         Using acTrans As Transaction = sourceDb.TransactionManager.StartTransaction()
  10.  
  11.             ' Open the block table
  12.             Dim bt As BlockTable = DirectCast(acTrans.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, False), BlockTable)
  13.             If bt.Has(_blockName) Then
  14.                 acObjIdColl.Add(bt(_blockName))
  15.  
  16.             End If
  17.         End Using
  18.  
  19.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  20.         Dim acCurDb As Database = acDoc.Database
  21.  
  22.         ' Lock the document
  23.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  24.  
  25.             ' Start a transaction in the database
  26.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  27.  
  28.                 ' Open the Block table for read
  29.                 Dim acBlkTblNewDoc As BlockTable
  30.                 acBlkTblNewDoc = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  31.  
  32.                 ' Open the Block table record Model space for read
  33.                 Dim acBlkTblRecNewDoc As BlockTableRecord
  34.                 acBlkTblRecNewDoc = acTrans.GetObject(acBlkTblNewDoc(BlockTableRecord.ModelSpace), OpenMode.ForRead)
  35.  
  36.                 ' Clone the objects to the new database
  37.                 Dim acIdMap As IdMapping = New IdMapping()
  38.                 acCurDb.WblockCloneObjects(acObjIdColl, acBlkTblRecNewDoc.ObjectId, acIdMap, DuplicateRecordCloning.Ignore, False)
  39.  
  40.                 ' Commit the transaction
  41.                 acTrans.Commit()
  42.  
  43.             End Using ' Dispose of the transaction
  44.         End Using ' Unlock the document
  45.  
  46.         'Return ObjectId
  47.         Return acObjIdColl(0)
  48.  
  49.     End Function
  50.  

*Edit used syntax in Code-blocks*
« Last Edit: October 30, 2013, 04:03:05 AM by Bert »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Create new BlockTableRecord, containing several others
« Reply #6 on: October 29, 2013, 01:33:52 PM »
SOOO much easier to read code when it's posted under the language used...  I think the problem is in your import function.  You've copied it almost exactly out of the developers guide, but consider that in the developers guide they are copying circles into the new document, here we are copying blocks into the current document.  Try the changes I've made and see if that works
Code - vb.net: [Select]
  1. Shared Sub addDetailstoBlock(_detailBlocksArray As List(Of String))
  2.         ' Get the current document and database
  3.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  4.         Dim acCurDb As Database = acDoc.Database
  5.         ' Lock the document
  6.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  7.             ' Start a transaction in the database
  8.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  9.                 ' Get the Block table for the current database
  10.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  11.                 ' Get the ModelSpace
  12.                 Dim modelSpace As BlockTableRecord = DirectCast(acTrans.GetObject(acBlockTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
  13.                 ' Get a fresh Name for our new DetailBlock (getDetailBlockSeqNum retrieves a Sequence number)
  14.                 Dim blkName As String = "DetailBlock_" & getDetailBlockSeqNum()
  15.                 ' Create the BlockTableRecord
  16.                 Dim acBlkTblRecId As ObjectId = Acad.findOrCreateBlockTableRecord(blkName).ObjectId
  17.                 ' Get the newly created BlockTableRecord
  18.                 Dim acBlkTblRec As BlockTableRecord = DirectCast(acTrans.GetObject(acBlkTblRecId, OpenMode.ForWrite), BlockTableRecord)
  19.  
  20.                 'Declare a insertionPoint for inserting the 'sub-blocks'
  21.                 Dim subBlkInsPnt As New Point3d(0, 0, 0)
  22.                 'Traverse the _detailBlocksArray wich holds all desired 'sub-blocks'-names
  23.                 For Each subBlockName As String In _detailBlocksArray
  24.                     'searchOrImportBlock checks if the desired 'sub-block' exists in Block table current database, if not it imports it from a librairy-DWG
  25.                     'either way, this gives us a ObjectId of the desired 'sub-block'
  26.                     Dim subBlkId As ObjectId = Acad.searchOrImportBlock(Acad.MontageDetails, subBlockName)
  27.                     'Create a BlockReference using the subBlkInsPnt & subBlkId
  28.                     Dim subBlkRef As BlockReference = New BlockReference(subBlkInsPnt, subBlkId)
  29.                     'Add the subBlkRef to our 'main' BlockTableRecord
  30.                     acBlkTblRec.AppendEntity(subBlkRef)
  31.                     acTrans.AddNewlyCreatedDBObject(subBlkRef, True)
  32.                     'Increment the X-value of our subBlkInsPnt
  33.                     Dim blkEx As Extents3d = subBlkRef.GeometricExtents()
  34.                     subBlkInsPnt = New Point3d(subBlkInsPnt.X + Math.Abs(blkEx.MinPoint.X - blkEx.MaxPoint.X), subBlkInsPnt.Y, 0)
  35.                 Next
  36.  
  37.                 'Determine a insertionPoint for our 'main' BlockReference
  38.                 insPnt = New Point3d(-15000, 15000, 0)
  39.  
  40.                 'Create and add our 'main' BlockReference to the ModelSpace
  41.                 Dim acBlkRef As New BlockReference(insPnt, acBlkTblRecId)
  42.                 modelSpace.AppendEntity(acBlkRef)
  43.                 acTrans.AddNewlyCreatedDBObject(acBlkRef, True)
  44.  
  45.                 ' Commit the transaction
  46.                 acTrans.Commit()
  47.             End Using ' Dispose of the transaction
  48.         End Using ' Unlock the document
  49.     End Sub
  50. Shared Function findOrCreateBlockTableRecord(_blockName As String) As BlockTableRecord
  51.  
  52.         ' Get the current document and database, and start a transaction
  53.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  54.         Dim acCurDb As Database = acDoc.Database
  55.         ' Lock the document
  56.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  57.             ' Start a transaction in the database
  58.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  59.                 ' Get the Block table for the current database
  60.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  61.  
  62.                 ' If found in local BlockTableRecord
  63.                 If acBlockTbl.Has(_blockName) Then
  64.                     Dim rtnBlock As BlockTableRecord
  65.                     rtnBlock = acTrans.GetObject(acBlockTbl(_blockName), OpenMode.ForWrite)
  66.                     Return rtnBlock
  67.                 Else
  68.                     ' Create our new block table record...
  69.                     Dim rtnBlock As New BlockTableRecord
  70.                     ' ... and set its properties
  71.                     rtnBlock.Name = _blockName
  72.                     ' Add the new block to the block table
  73.                     acBlockTbl.UpgradeOpen()
  74.  
  75.                     Dim btrId As ObjectId = acBlockTbl.Add(rtnBlock)
  76.                     acTrans.AddNewlyCreatedDBObject(rtnBlock, True)
  77.                     ' Commit the transaction
  78.                     acTrans.Commit()
  79.                     Return rtnBlock
  80.                 End If
  81.  
  82.             End Using ' Dispose of the transaction
  83.         End Using ' Unlock the document
  84.  
  85.     End Function
  86. Shared Function searchOrImportBlock(_sourceFileName As String, _blockName As String) As ObjectId
  87.         ' Get the current document and database
  88.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  89.         Dim acCurDb As Database = acDoc.Database
  90.         ' Lock the document
  91.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  92.             ' Start a transaction in the database
  93.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  94.                 ' Get the Block table for the current database
  95.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  96.  
  97.                 ' If found in local BlockTableRecord
  98.                 If Not acBlockTbl.Has(_blockName) Then
  99.                         ' If not found in local BlockTableRecord then Import from _sourceFileName
  100.                         importBlockFromSourceFile(_sourceFileName, _blockName)
  101.                 End If
  102.                     Return acBlockTbl(_blockName)
  103.             End Using ' Dispose of the transaction
  104.         End Using ' Unlock the document
  105.  
  106.  
  107.     End Function
  108. Shared Function importBlockFromSourceFile(_sourceFileName As String, _blockName As String)
  109.  
  110.         Dim sourceDb As New Database(False, True)
  111.         Dim acObjIdColl As ObjectIdCollection = New ObjectIdCollection()
  112.  
  113.         ' Read the DWG into a side database
  114.         sourceDb.ReadDwgFile(_sourceFileName, System.IO.FileShare.Read, True, "")
  115.  
  116.         Using acTrans As Transaction = sourceDb.TransactionManager.StartTransaction()
  117.  
  118.             ' Open the block table
  119.             Dim bt As BlockTable = DirectCast(acTrans.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, False), BlockTable)
  120.             If bt.Has(_blockName) Then
  121.                 acObjIdColl.Add(bt(_blockName))
  122.  
  123.             End If
  124.         End Using
  125.  
  126.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  127.         Dim acCurDb As Database = acDoc.Database
  128.  
  129.         ' Lock the document
  130.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  131.  
  132.             ' Start a transaction in the database
  133.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  134.  
  135.                 ' Open the Block table for read
  136.                 ' Useless Dim acBlkTblNewDoc As BlockTable
  137.                 ' Useless acBlkTblNewDoc = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  138.  
  139.                 ' Open the Block table record Model space for read
  140.                 ' Useless Dim acBlkTblRecNewDoc As BlockTableRecord
  141.                 ' Useless acBlkTblRecNewDoc = acTrans.GetObject(acBlkTblNewDoc(BlockTableRecord.ModelSpace), OpenMode.ForRead)
  142.  
  143.                 ' Clone the objects to the new database
  144.                 Dim acIdMap As IdMapping = New IdMapping()
  145.                 acCurDb.WblockCloneObjects(acObjIdColl, acCurDb.BlockTableId, acIdMap, DuplicateRecordCloning.Ignore, False)
  146.  
  147.                 ' Commit the transaction
  148.                 acTrans.Commit()
  149.  
  150.             End Using ' Dispose of the transaction
  151.         End Using ' Unlock the document
  152.  
  153.         'Return ObjectId
  154.         ' This was the ObjectId of the btr in the original database... Return acObjIdColl(0)
  155.  
  156.     End Function
  157.  

you're basically doing the same as Kean in his post http://through-the-interface.typepad.com/through_the_interface/2006/08/import_blocks_f.html

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #7 on: October 30, 2013, 03:48:17 AM »
Hello WILL,

You're absolutely right about posting the code under the language used ! I'll do this from now on.

I've made the suggested changes to my code but run into problems trying to Insert the Blocks into ModelSpace later on :
Code - vb.net: [Select]
  1. 'I make sure the Block is available via searchOrImportBlock, as discussed above
  2. Dim acadBlockId As ObjectId = Acad.searchOrImportBlock(Acad.SketchblocksFileName, "SketchVBNBlock")
  3. ' Next I insert it into ModelSpace using InsertBlock
  4. Dim acadblockref As BlockReference = Acad.InsertBlock("SketchVBNBlock", Me.geoInsPnt, Me.dimBreedte + (Me.dimPaneelDikte * 2), Me.dimLengte + (Me.dimPaneelDikte * 2), Me.dimPaneelDikte, 0)
  5.  
  6. Shared Function InsertBlock(ByVal _blockName As String, ByVal insPt As Point3d, ByVal xBlkScale As Double, _
  7.                                 ByVal yBlkScale As Double, ByVal zBlkScale As Double, ByVal ang As Double) As BlockReference
  8.         ' Get the current document and database
  9.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  10.         Dim acCurDb As Database = acDoc.Database
  11.  
  12.         ' Lock the document
  13.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  14.  
  15.             ' Start a transaction in the database
  16.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  17.  
  18.                 Dim blkTable As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  19.                 If blkTable.Has(_blockName) Then
  20.                     Dim blkObjId As ObjectId = blkTable(_blockName)
  21.                     Dim blkRef As BlockReference = New BlockReference(insPt, blkObjId)
  22.                     blkRef.SetDatabaseDefaults()
  23.                     blkRef.ScaleFactors = New Scale3d(xBlkScale, yBlkScale, zBlkScale)
  24.                     blkRef.Rotation = ang
  25.  
  26.                     Dim blkTblRec As BlockTableRecord
  27.                     ' Assumes the current space was already changed to.
  28.                     blkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite)
  29.  
  30.                     blkTblRec.AppendEntity(blkRef)
  31.                     acTrans.AddNewlyCreatedDBObject(blkRef, True)
  32.  
  33.  
  34.                     ' add the attribute definitions.
  35.                     Dim blkTblR As BlockTableRecord = blkObjId.GetObject(OpenMode.ForRead)
  36.                     For Each objId As ObjectId In blkTblR
  37.                         Dim obj As DBObject = objId.GetObject(OpenMode.ForRead)
  38.                         If TypeOf obj Is AttributeDefinition Then
  39.                             Dim ad As AttributeDefinition = objId.GetObject(OpenMode.ForRead)
  40.                             Dim ar As AttributeReference = New AttributeReference()
  41.                             ar.SetAttributeFromBlock(ad, blkRef.BlockTransform)
  42.                             ar.Position = ad.Position.TransformBy(blkRef.BlockTransform)
  43.                             blkRef.AttributeCollection.AppendAttribute(ar)
  44.                             acTrans.AddNewlyCreatedDBObject(ar, True)
  45.                         End If
  46.                     Next
  47.  
  48.                     ' Commit the transaction
  49.                     acTrans.Commit()
  50.                     Return blkRef
  51.                 End If
  52.  
  53.             End Using ' Dispose of the transaction
  54.         End Using ' Unlock the document
  55.  
  56.         Return Nothing
  57.  
  58.     End Function
  59.  

Code fails the test "blkTable.Has(_blockName)" on line 19, and this exits the function with a 'null-return'.
with the previous version of searchOrImportBlock "blkTable.Has(_blockName)" was allways met ?
« Last Edit: October 30, 2013, 04:22:47 AM by Bert »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Create new BlockTableRecord, containing several others
« Reply #8 on: October 30, 2013, 03:36:50 PM »
Ok, so the import function isn't working properly then?

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #9 on: October 31, 2013, 03:13:28 AM »
Why yes, it now looks like this :

Code - vb.net: [Select]
  1.     Shared Sub importBlockFromSourceFile(_sourceFileName As String, _blockName As String)
  2.  
  3.         Dim acSourceDb As New Database(False, True)
  4.         Dim acObjIdColl As ObjectIdCollection = New ObjectIdCollection()
  5.  
  6.         ' Read the DWG into a side database
  7.         acSourceDb.ReadDwgFile(_sourceFileName, System.IO.FileShare.Read, True, "")
  8.  
  9.         Using acTrans As Transaction = acSourceDb.TransactionManager.StartTransaction()
  10.             ' Open the block table
  11.             Dim acBlkTblSourceDoc As BlockTable = DirectCast(acTrans.GetObject(acSourceDb.BlockTableId, OpenMode.ForRead, False), BlockTable)
  12.             If acBlkTblSourceDoc.Has(_blockName) Then acObjIdColl.Add(acBlkTblSourceDoc(_blockName))
  13.         End Using
  14.  
  15.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  16.         Dim acCurDb As Database = acDoc.Database
  17.  
  18.         ' Lock the document
  19.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  20.             ' Start a transaction in the database
  21.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  22.  
  23.                 ' Clone the objects to the new database
  24.                 Dim acIdMap As IdMapping = New IdMapping()
  25.                 acCurDb.WblockCloneObjects(acObjIdColl, acCurDb.BlockTableId, acIdMap, DuplicateRecordCloning.Ignore, False)
  26.  
  27.                 ' Commit the transaction
  28.                 acTrans.Commit()
  29.  
  30.             End Using ' Dispose of the transaction
  31.         End Using ' Unlock the document
  32.  
  33.     End Sub

But when looking up the newly imported block in the next function, it isn't found :
Code - vb.net: [Select]
  1.     Dim blkTable As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  2.     If blkTable.Has(_blockName) Then '---> is false, should be true
  3.  

Any ideas ?
Thanks !

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Create new BlockTableRecord, containing several others
« Reply #10 on: October 31, 2013, 08:02:35 AM »
Quote
acCurDb.WblockCloneObjects(acObjIdColl, acCurDb.BlockTableId, acIdMap, DuplicateRecordCloning.Ignore, False)

From Database.WBlockCloneObjexts in ObjectArx reference
Quote
id can only be an BlockTableRecord, Dictionary, or SymbolTable object. In multiple calls, the owners must be from the same destination database. If the owner is a dictionary, newly cloned entries are set as anonymous.

Looks like your trying to use a BlockTable where you should be using a BlockTableRecord.  Create the BlockTableRecord in the destination Database first then use it's ObjectId in the WBlockCloneObjects method.
Revit 2019, AMEP 2019 64bit Win 10

owenwengerd

  • Bull Frog
  • Posts: 451
Re: Create new BlockTableRecord, containing several others
« Reply #11 on: October 31, 2013, 12:20:44 PM »
Looks like your trying to use a BlockTable where you should be using a BlockTableRecord.

A BlockTable is allowed (it is a SymbolTable).

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Create new BlockTableRecord, containing several others
« Reply #12 on: October 31, 2013, 01:46:17 PM »
The block table is a SymbolTable, this should work...

I'd suggest you confirm that the import function is working by returning the result of line 12: acBlkTblSourceDoc.Has(_blockName)

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #13 on: November 04, 2013, 04:09:53 AM »
So, I'm back @ work from a 3 day leave and have fresh hopes on fixing this one  :wink: !

What I've got now :
Code - vb.net: [Select]
  1.     'This is dummy-code, but an exact representation of how I insert a BlockReference in ModelSpace.
  2.         ' I allways do this as follows, whenever and wherever I need to troughout my code.
  3.         ' searchOrImportBlock makes sure i've got the desired Block in my BlockTable
  4.         ' InsertBlock creates a BlockReference from that desired Block -----> and this doesn't check out ..
  5.     insPnt = New Point3d(Xval, Yval, 0)
  6.     acadBlockId = Acad.searchOrImportBlock(Acad.SketchblocksFileName, "SketchHoekBlock V")
  7.     acadblockref = Acad.InsertBlock("SketchHoekBlock V", insPnt, Me.dimPaneelDikte, Me.dimPaneelDikte, HoekHoogte, 0)
  8.     AttVals.Clear()
  9.     AttVals.Add("InventTransId|" & Me.genInventTransId)
  10.     AttVals.Add("TYPE|" & Me.genType & " 2T")
  11.     'AttVals.Add("TYPE|" & Replace(Me.genType, "D", "H") & " 2T")
  12.     Acad.set_BlockAttributes(acadblockref, AttVals)
  13.  

'searchOrImportBlock
Code - vb.net: [Select]
  1.         Shared Function searchOrImportBlock(_sourceFileName As String, _blockName As String) As ObjectId
  2.         ' Get the current document and database
  3.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  4.         Dim acCurDb As Database = acDoc.Database
  5.         ' Lock the document
  6.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  7.             ' Start a transaction in the database
  8.             Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
  9.                 ' Get the Block table for the current database
  10.                 Dim acBlockTbl As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  11.  
  12.                 ' If found in local BlockTableRecord
  13.                 If Not acBlockTbl.Has(_blockName) Then
  14.                     ' If not found in local BlockTableRecord then Import from _sourceFileName
  15.                     ' importBlockFromSourceFile now set to return a boolean if successfull.
  16.                          ' if it wasnt, this function returns 'Nothing'
  17.                     If Not importBlockFromSourceFile(_sourceFileName, _blockName) Then Return Nothing
  18.                 End If
  19.                 Return acBlockTbl(_blockName) ' ----> but all is well, and the ObjectId of the newly acquired Block get returned
  20.             End Using ' Dispose of the transaction
  21.         End Using ' Unlock the document
  22.     End Function
  23.  

' importBlockFromSourceFile is now set to return a Boolean
Code - vb.net: [Select]
  1.  Shared Function importBlockFromSourceFile(_sourceFileName As String, _blockName As String) As Boolean
  2.  
  3.         Dim rtnBool As Boolean = False
  4.         Dim acSourceDb As New Database(False, True)
  5.         Dim acObjIdColl As ObjectIdCollection = New ObjectIdCollection()
  6.  
  7.         ' Read the DWG into a side database
  8.         acSourceDb.ReadDwgFile(_sourceFileName, System.IO.FileShare.Read, True, "")
  9.  
  10.         Using acTrans As Transaction = acSourceDb.TransactionManager.StartTransaction()
  11.             ' Open the block table
  12.             Dim acBlkTblSourceDoc As BlockTable = DirectCast(acTrans.GetObject(acSourceDb.BlockTableId, OpenMode.ForRead, False), BlockTable)
  13.             ' Set the rtnBool wheter or not the desired block was found in the Source DWG
  14.             rtnBool = acBlkTblSourceDoc.Has(_blockName)  '-----> this returns TRUE in test
  15.             ' If it is, copy the desired block to our ObjectIdCollection
  16.             If rtnBool Then acObjIdColl.Add(acBlkTblSourceDoc(_blockName)) '-----> thus it get copied
  17.         End Using
  18.  
  19.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  20.         Dim acCurDb As Database = acDoc.Database
  21.  
  22.         ' Lock the document
  23.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  24.             ' Start a transaction in the database
  25.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  26.  
  27.                 ' Clone the objects to the new database
  28.                 Dim acIdMap As IdMapping = New IdMapping()
  29.                 acCurDb.WblockCloneObjects(acObjIdColl, acCurDb.BlockTableId, acIdMap, DuplicateRecordCloning.Ignore, False)
  30.  
  31.                 ' Commit the transaction
  32.                 acTrans.Commit()
  33.  
  34.             End Using ' Dispose of the transaction
  35.         End Using ' Unlock the document
  36.  
  37.         Return rtnBool
  38.     End Function
  39.  

Till this point, all is well. But when I try to create a BlockReference from the newly cloned/added Block, it doesn't find it anymore :
Code - vb.net: [Select]
  1. Shared Function InsertBlock(ByVal _blockName As String, ByVal insPt As Point3d, ByVal xBlkScale As Double, _
  2.                                 ByVal yBlkScale As Double, ByVal zBlkScale As Double, ByVal ang As Double) As BlockReference
  3.         ' Get the current document and database
  4.         Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
  5.         Dim acCurDb As Database = acDoc.Database
  6.  
  7.         ' Lock the document
  8.         Using acLckDoc As DocumentLock = acDoc.LockDocument()
  9.  
  10.             ' Start a transaction in the database
  11.             Using acTrans = acDoc.TransactionManager.StartTransaction()
  12.  
  13.                 Dim blkTable As BlockTable = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead)
  14.                 If blkTable.Has(_blockName) Then '------> this check allways fails now.
  15.                     Dim blkObjId As ObjectId = blkTable(_blockName)
  16.                     Dim blkRef As BlockReference = New BlockReference(insPt, blkObjId)
  17.                     blkRef.SetDatabaseDefaults()
  18.                     blkRef.ScaleFactors = New Scale3d(xBlkScale, yBlkScale, zBlkScale)
  19.                     blkRef.Rotation = ang
  20.  
  21.                     Dim blkTblRec As BlockTableRecord
  22.                     ' Assumes the current space was already changed to.
  23.                     blkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite)
  24.  
  25.                     blkTblRec.AppendEntity(blkRef)
  26.                     acTrans.AddNewlyCreatedDBObject(blkRef, True)
  27.  
  28.  
  29.                     ' add the attribute definitions.
  30.                     Dim blkTblR As BlockTableRecord = blkObjId.GetObject(OpenMode.ForRead)
  31.                     For Each objId As ObjectId In blkTblR
  32.                         Dim obj As DBObject = objId.GetObject(OpenMode.ForRead)
  33.                         If TypeOf obj Is AttributeDefinition Then
  34.                             Dim ad As AttributeDefinition = objId.GetObject(OpenMode.ForRead)
  35.                             Dim ar As AttributeReference = New AttributeReference()
  36.                             ar.SetAttributeFromBlock(ad, blkRef.BlockTransform)
  37.                             ar.Position = ad.Position.TransformBy(blkRef.BlockTransform)
  38.                             blkRef.AttributeCollection.AppendAttribute(ar)
  39.                             acTrans.AddNewlyCreatedDBObject(ar, True)
  40.                         End If
  41.                     Next
  42.  
  43.                     ' Commit the transaction
  44.                     acTrans.Commit()
  45.                     Return blkRef
  46.                 End If
  47.  
  48.             End Using ' Dispose of the transaction
  49.         End Using ' Unlock the document
  50.  
  51.         Return Nothing
  52.  
  53.     End Function
  54.  
« Last Edit: November 04, 2013, 04:16:39 AM by Bert »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Create new BlockTableRecord, containing several others
« Reply #14 on: November 04, 2013, 06:43:16 PM »
what happens if you commit the transaction in searchOrImportBlock before returning the BTR ObjectId?

Bert

  • Guest
Re: Create new BlockTableRecord, containing several others
« Reply #15 on: November 05, 2013, 02:20:57 AM »
That's it ! That did the trick !

I tip my hat to you kind Sirs.
Thank all of you who helped think and gave feedback.

I have to say I find it a bit odd that committing my transaction there made any difference ..

Anyways, thanks again and i'll catch y'all on some other mystery!

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Create new BlockTableRecord, containing several others
« Reply #16 on: November 05, 2013, 12:44:47 PM »
Me thinks you should do some reading on transactions and why we use them.  If you do not commit (implicit abort) everything you did inside that transaction gets undone.  My habit is to create the using statement with and add the commit line before I do anything else because I've made the same mistake in the past.