Author Topic: Why is the block not changed after WblockClone?  (Read 2837 times)

0 Members and 1 Guest are viewing this topic.

mgreven

  • Guest
Why is the block not changed after WblockClone?
« on: March 07, 2012, 02:59:11 AM »
I have made a routine to WblockClone blocks between drawings...
When i use the routine and "Wblock" blocks from a drawing with "updatet blocks" to the current drawing, the blocks in the current drawing do not change.
In de code below i use: "DuplicateRecordCloning.Replace" which should replace the existing blocks with the Cloning ones?

What is wrong here?

        Sub WblockBlock(ByVal SourceDrawing As String)
            ' Connect to Current Drawing
            Dim MyDWG As Autodesk.AutoCAD.ApplicationServices.Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
            Dim MyDB As Autodesk.AutoCAD.DatabaseServices.Database = MyDWG.Database
            Dim MyTransMan As Autodesk.AutoCAD.DatabaseServices.TransactionManager = MyDWG.TransactionManager
            ' Connect to Source Drawing
            Using SrcDB As Autodesk.AutoCAD.DatabaseServices.Database = New Autodesk.AutoCAD.DatabaseServices.Database(False, True)
                ' Read Source Drawing
                SrcDB.ReadDwgFile(SourceDrawing, IO.FileShare.ReadWrite, True, "")
                ' Start Transactions
                Dim SrcTransMan As Autodesk.AutoCAD.DatabaseServices.TransactionManager = SrcDB.TransactionManager
                Using SrcTrans As Autodesk.AutoCAD.DatabaseServices.Transaction = SrcTransMan.StartTransaction,
                        MyTrans As Autodesk.AutoCAD.DatabaseServices.Transaction = MyTransMan.StartTransaction

                    ' Open de source BlockTable...
                    Dim SrcBT As BlockTable = SrcDB.BlockTableId.GetObject(OpenMode.ForRead)

                    ' Make the BlockCollection...
                    Dim MyBlkColl As New Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection ' Colection that Stores the Entities ID

                    ' Get the collection of MvBlocks...
                    ' Loop entities in Source Dictionary
                    Dim ObjId As ObjectId
                    For Each ObjId In SrcBT
                        Dim MyBTR As BlockTableRecord = SrcTrans.GetObject(ObjId, OpenMode.ForRead)

                        If Not MyBTR.IsAnonymous And Not MyBTR.IsLayout And Not MyBTR.IsFromExternalReference And _
                                Not MyBTR.IsFromOverlayReference And Not MyBTR.Name.StartsWith("Aec", StringComparison.OrdinalIgnoreCase) Then
                            ' The Block Name is according filter...
                            MyBlkColl.Add(ObjId)
                        End If
                    Next

                    ' Open de destination BlockTable...
                    Dim MyBT As BlockTable = MyDB.BlockTableId.GetObject(OpenMode.ForRead)

                    ' WblockClone the Source Entitys
                    Dim myMap As New Autodesk.AutoCAD.DatabaseServices.IdMapping
                    MyDB.WblockCloneObjects(MyBlkColl, MyBT.Id, myMap, Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning.Replace, False)

                    ' Commit, Close and Dispose Transactions
                    MyTrans.Commit()
                    SrcTrans.Commit()
                End Using
            End Using
        End Sub



TheMaster

  • Guest
Re: Why is the block not changed after WblockClone?
« Reply #1 on: March 08, 2012, 12:20:56 AM »
I have made a routine to WblockClone blocks between drawings...
When i use the routine and "Wblock" blocks from a drawing with "updatet blocks" to the current drawing, the blocks in the current drawing do not change.
In de code below i use: "DuplicateRecordCloning.Replace" which should replace the existing blocks with the Cloning ones?

What is wrong here?



I don't believe 'DuplicateRecordCloning' applies to blocks or nested blocks, and cloning multiple block definitions would require multiple calls to the clone function (be it WblockCloneObjects or DeepCloneObjects).

From the ObjectARX docs:

Quote

When the objects to be cloned do not all have the same owner, then it is necessary to make up individual AcDbObjectIdArray entities, each one containing all the objects with the same owner. The wblockCloneObjects() method is then called once for each array, with the deferXlation argument set to true for all but the last call, so that ID translation will be done to complete the cloning. Refer to the ObjectARX Developer's Guide for more information on the use of this function.


The way I've done it, is to first rename the referenced blocks that I want redefined in the destination drawing, then deep clone the referencing block (which will bring in the referenced block(s) along with it), and then erase the old, renamed blocks in the desintation drawing.

When you clone a block that references another block (let's call the referenced block 'nested') that also exists in the destination drawing, the nested block's definition is not cloned also. You have to either do it manually for each referenced block, or do the rename/clone/erase trick. Whether you can do that easily or not depends on what other things in the destination drawing reference the nested block(s) you want to redefine. If there are any of those, then you must go through them and change the ids that point to the renamed old version so they point to the new version that was brought in by cloning.

So, let's say that in the destination drawing you have Block 'A', which contains an insertion of block 'B'.

If you want to redefine both A and B in one swell foop (deep clone), then what you can do is rename B to 'OLD_B' (or whatever) in the destination drawing, and then deepclone the new version of 'A' from the source drawing into the destination drawing.

That should cause both A and B to be deep-cloned into the destination drawing.

Before you can delete OLD_B, you must find all references to it in the destination drawing, and change them to reference the new version of 'B' that came along for the ride with the new version of 'A' when it was deep-cloned into the destination drawing.

Gabeesh ?

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Why is the block not changed after WblockClone?
« Reply #2 on: March 08, 2012, 02:59:35 PM »
Could you rename them, clone them, then use DBObject.SwapIdWith?

TheMaster

  • Guest
Re: Why is the block not changed after WblockClone?
« Reply #3 on: March 09, 2012, 04:02:47 AM »
Could you rename them, clone them, then use DBObject.SwapIdWith?

I don't recall the exact details, but do remember that when I tried it there was a problem, and I'm pretty sure it had something to do with the fact that the BlockBegin object stores a list of the ObjecIds of all BlockReferences (that's what GetBlockReferenceIds() returns).

So, if you do a SwapIdWith() on the BlockTableRecords, the result is that the BlockBegin object's list of block reference ids is no longer valid, or at least, that's what I saw when I tried it, which I think was in AutoCAD 2007 or 2008, so I won't commit to saying SwapIdWith() won't work now (in AutoCAD 2012), before trying it and looking at what GetBlockReferenceIds() returns.

Also remember that AutoCAD stores some things in a BlockTableRecord's xdata and/or xdictionary (relating to insertion units if I"m not mistaken), so swapping xdata and/or xdictionaries would effectively swap that information too.
« Last Edit: March 09, 2012, 04:42:37 AM by TheMaster »

kaefer

  • Guest
Re: Why is the block not changed after WblockClone?
« Reply #4 on: March 09, 2012, 06:11:52 AM »
Well, I'm not convinced that the problem is necessarily with WBlockCloneObjects. I tried to demo a simple case: create two blocks, nested, such that there is an outer and an inner one, and see what happens if the outer is cloned into another drawing.

Surprisingly, the difficulty I experienced had nothing to do with the cloning, but with the apparently simple task of replacing the inner BlockTableRecord in the target drawing. Even that goes well without temporary renaming, but only reassigning the existing references.

My observation from the accompanying code sample is that WBlockCloneObjects performs as expected - at least in this simple case and on an up-to-date AutoCAD version: The nested BlockTableReocrd is dutifully replaced by the cloning operation, regardless if an instance of it already exists or not.

Apologies for coding in F#.

Code - F#: [Select]
  1. // Create a new BlockTableRecord.
  2. // If one with the same name already exists, get its references and
  3. // erase it. After creation of the new one, change those references
  4. // so that they point to the new instead of the old BlockTableRecord.
  5. let createBlock (db : Database) (name : string) ent =
  6.     let tm = db.TransactionManager
  7.     let bt = db.BlockTableId.GetObject OpenMode.ForWrite :?> BlockTable
  8.     let refIds =
  9.         if bt.Has name then
  10.             let btr = bt.[name].GetObject OpenMode.ForWrite :?> BlockTableRecord
  11.             let refIds = btr.GetBlockReferenceIds(true, false)
  12.             btr.Erase()
  13.             refIds
  14.         else new ObjectIdCollection()
  15.     let btr = new BlockTableRecord(Name = name)
  16.     let btrId = bt.Add btr
  17.     tm.AddNewlyCreatedDBObject(btr, true)
  18.     btr.AppendEntity ent |> ignore
  19.     tm.AddNewlyCreatedDBObject(ent, true)
  20.     for refId in refIds do
  21.         let bref = refId.GetObject OpenMode.ForWrite :?> BlockReference
  22.         bref.BlockTableRecord <- btrId
  23.     btrId
  24.  
  25. [<CommandMethod("WblockCloneObjectsTest")>]
  26. let wblockCloneObjectsTest() =
  27.    
  28.     let doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
  29.     let db = doc.Database
  30.     let ed = doc.Editor
  31.  
  32.     let getEnter (msg : string) =
  33.         let pr = ed.GetString msg
  34.         pr.Status = PromptStatus.OK
  35.            
  36.     use srcDb = new Database(true, true)
  37.     use srcTr = srcDb.TransactionManager.StartTransaction()
  38.  
  39.     // Create nested blocks in side Database. The inner contains a circle.
  40.     let srcNestedBtrId =
  41.         new Circle(Point3d(0.5, 0.5, 0.), Vector3d.ZAxis, 0.5)
  42.         |> createBlock srcDb "NestedBlock"
  43.     let srcBtrId =
  44.         new BlockReference(Point3d.Origin, srcNestedBtrId)
  45.         |> createBlock srcDb "Block"
  46.     srcTr.Commit()
  47.  
  48.     // Clone them into the active drawing
  49.     db.WblockCloneObjects(
  50.         new ObjectIdCollection[| srcBtrId |],
  51.         db.BlockTableId,
  52.         new IdMapping(),
  53.         DuplicateRecordCloning.Replace,
  54.         false )
  55.    
  56.     // Create a reference to the outer block.
  57.     (
  58.         use tr = db.TransactionManager.StartTransaction()
  59.         let bt = db.BlockTableId.GetObject OpenMode.ForRead :?> BlockTable
  60.         let cs = db.CurrentSpaceId.GetObject OpenMode.ForWrite:?> BlockTableRecord
  61.         let bref = new BlockReference(Point3d.Origin, bt.["Block"])
  62.         cs.AppendEntity bref |> ignore
  63.         tr.AddNewlyCreatedDBObject(bref, true)
  64.         tr.Commit()
  65.     )
  66.     if getEnter "You see a Circle. Press Enter to continue" then
  67.         // Replace the inner block with one containing a line
  68.         (
  69.             use tr = db.TransactionManager.StartTransaction()
  70.             new Line(Point3d(0., 0., 0.), Point3d(1., 1., 1.))
  71.             |> createBlock db "NestedBlock"
  72.             |> ignore
  73.             tr.Commit()
  74.         )
  75.         ed.Regen()
  76.  
  77.         if getEnter "You see a Line. Press Enter to continue" then
  78.             // Clone them again into the active drawing, replacing the inner block
  79.             db.WblockCloneObjects(
  80.                 new ObjectIdCollection[| srcBtrId |],
  81.                 db.BlockTableId,
  82.                 new IdMapping(),
  83.                 DuplicateRecordCloning.Replace,
  84.                 false )