Author Topic: Definitive example of how to replace an existing blockreference with Attributes  (Read 2406 times)

0 Members and 1 Guest are viewing this topic.

vegbruiser

  • Guest
Hi Folks,

I've been looking here, here and here at examples showing how to replace/redefine an existing blockdefinition, but my implementation so far (based on this) doesn't come anywhere close to the same results I see if I do it manually. (insert existing block I know to be in the drawing, select redefine and then run the AttSync command)

I realise I'm not returning an ObjectId from this function, and I was wondering what to do about that (since it will probably have changed during this function)? FWIW the objectId I pass to this method is for an existing drawing frame blockreference which I need to redefine to account for some changes I've made to the attributedefinitions contained within. I changed some ATTDEFS from single to multiline to better fit the frame.

Code - vb.net: [Select]
  1. Private Function ReplaceBlock(objectId As ObjectId) As ObjectId
  2.             Dim Tx As Transaction = Active.Database.TransactionManager.StartTransaction()
  3.             Dim blkDb As Database = New Database(False, True)
  4.             Try
  5.                 Dim tmpblkref As BlockReference = Tx.GetObject(objectId, OpenMode.ForRead)
  6.                 blkDb.ReadDwgFile("C:\VAULT WORKING FOLDER\Designs\Job\DRAWING FRAMES\" & tmpblkref.Name & ".dwg", System.IO.FileShare.Read, True, "")
  7.                 Dim blockTable As BlockTable = Tx.GetObject(Active.Database.BlockTableId, OpenMode.ForRead, False, True)
  8.                 Dim btrId As ObjectId = Active.Database.Insert(tmpblkref.Name, blkDb, True)
  9.                 If btrId <> objectId.Null Then
  10.                     Dim btr As BlockTableRecord = Tx.GetObject(btrId, OpenMode.ForRead, False, True)
  11.                     Dim brefIds As ObjectIdCollection = btr.GetBlockReferenceIds(False, True)
  12.                     For Each id As ObjectId In brefIds
  13.                         Dim bref As BlockReference =
  14.                         Tx.GetObject(id, OpenMode.ForWrite, False, True)
  15.                         bref.RecordGraphicsModified(True)
  16.                     Next
  17.                 End If
  18.             Catch ex As Exception
  19.                 Tx.Commit()
  20.                 Tx.Dispose()
  21.                 blkDb.Dispose()
  22.             End Try
  23.         End Function

Thanks in advance.

Alex.

MexicanCustard

  • Swamp Rat
  • Posts: 705
What have you defined as "Active"? Current Application?

Were you going to extract a block from "blkDB". Because I don't see any code to do that.  Looks like you're using the "Active" database for all your transactions.

You can redefine an existing block simply by opening the blockdefinition and replacing/adding/redefining all the objectIds contained within that blockdefinition.  The first link you provided shows this at the bottom of the example.

If I had to guess what you want to do is open the block file to memory, open the block file BlockTableRecord,   clear the ObjectIdCollection of the existing BlockTableRecord , then WBlock the block file BlockTableRecord ObjectIdCollection into the existing db BlockTableRecord.

Code - C#: [Select]
  1.         public static void ReplaceBlock(ObjectId objectId)
  2.         {
  3.             var db = Application.DocumentManager.MdiActiveDocument.Database;
  4.             using (var tr = db.TransactionManager.StartTransaction())
  5.             {
  6.                 var tmpblkref = (BlockTableRecord)tr.GetObject(objectId, OpenMode.ForWrite);
  7.                 foreach (var id in tmpblkref)
  8.                 {
  9.                     var obj = tr.GetObject(id, OpenMode.ForWrite);
  10.                     obj.Erase();
  11.                 }
  12.  
  13.                 var blkDb = new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
  14.                 blkDb.ReadDwgFile("C:\\VAULT WORKING FOLDER\\Designs\\Job\\DRAWING FRAMES\\" + tmpblkref.Name + ".dwg",
  15.                                   System.IO.FileShare.Read, true, "");
  16.                 using (var blkTr = blkDb.TransactionManager.StartTransaction())
  17.                 {
  18.                     var blkTbl = (BlockTable)blkTr.GetObject(blkDb.BlockTableId, OpenMode.ForRead);
  19.                     var btr = (BlockTableRecord)blkTr.GetObject(blkTbl[tmpblkref.Name], OpenMode.ForRead);
  20.                     var oids = new Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection(btr.Cast<ObjectId>().ToArray());
  21.  
  22.                     db.WblockCloneObjects(oids, tmpblkref.ObjectId, new IdMapping(), DuplicateRecordCloning.Replace, false);
  23.                     blkTr.Commit();
  24.                 }
  25.  
  26.                 tr.Commit();
  27.             }
  28.         }
Revit 2019, AMEP 2019 64bit Win 10

vegbruiser

  • Guest
Hi MC,

"Active" is from a helper class I got here somewhere but the link eludes me:

Code - vb.net: [Select]
  1. ''' <summary>
  2.     ''' Provides easy access to several "active" objects in the AutoCAD
  3.     ''' runtime environment.
  4.     ''' </summary>
  5.     Public NotInheritable Class Active
  6.         Private Sub New()
  7.         End Sub
  8.         ''' <summary>
  9.         ''' Returns the active Editor object.
  10.         ''' </summary>
  11.         Public Shared ReadOnly Property Editor() As Editor
  12.             Get
  13.                 Return Document.Editor
  14.             End Get
  15.         End Property
  16.  
  17.         ''' <summary>
  18.         ''' Returns the active Document object.
  19.         ''' </summary>
  20.         Public Shared ReadOnly Property Document() As Document
  21.             Get
  22.                 Return Application.DocumentManager.MdiActiveDocument
  23.             End Get
  24.         End Property
  25.  
  26.         ''' <summary>
  27.         ''' Returns the active Database object.
  28.         ''' </summary>
  29.         Public Shared ReadOnly Property Database() As Database
  30.             Get
  31.                 Return Document.Database
  32.             End Get
  33.         End Property
  34.  
  35.         ''' <summary>
  36.         ''' Sends a string to the command line in the active Editor
  37.         ''' </summary>
  38.         ''' <param name="message">The message to send.</param>
  39.         Public Shared Sub WriteMessage(message As String)
  40.             Editor.WriteMessage(message)
  41.         End Sub
  42.  
  43.         ''' <summary>
  44.         ''' Sends a string to the command line in the active Editor using String.Format.
  45.         ''' </summary>
  46.         ''' <param name="message">The message containing format specifications.</param>
  47.         ''' <param name="parameter">The variables to substitute into the format string.</param>
  48.         Public Shared Sub WriteMessage(message As String, ParamArray parameter As Object())
  49.             Editor.WriteMessage(message, parameter)
  50.         End Sub
  51.     End Class

blkDB is (a wblocked version of) the block I'm looking for; hence I only need to insert it into the currentDb:
Code: [Select]
Dim btrId As ObjectId = Active.Database.Insert(tmpblkref.Name, blkDb, True)

Redefining the block is great, but how would I keep the existing attributereference.textstring information in the current version of the blockreference?

Is it a case of doing the redefine as you said and then iterating through the attributecollection(s) of both old/new blockreferences, finding the matching attributedefinition.tag values (since they haven't changed!) and transferring the attributereference.textstring value from old to new?

or am I overthinking what needs to be done?

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Hi MC,

"Active" is from a helper class I got here somewhere but the link eludes me:

< .. >

You'd have gotten it from Scott McFarlane
http://au.autodesk.com/au-online/classes-on-demand/class-catalog/2013/autocad/dv2177
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

n.yuan

  • Bull Frog
  • Posts: 348
...
Code - vb.net: [Select]
  1. Private Function ReplaceBlock(objectId As ObjectId) As ObjectId
  2.             Dim Tx As Transaction = Active.Database.TransactionManager.StartTransaction()
  3.             Dim blkDb As Database = New Database(False, True)
  4.             Try
  5.                 Dim tmpblkref As BlockReference = Tx.GetObject(objectId, OpenMode.ForRead)
  6.                 blkDb.ReadDwgFile("C:\VAULT WORKING FOLDER\Designs\Job\DRAWING FRAMES\" & tmpblkref.Name & ".dwg", System.IO.FileShare.Read, True, "")
  7.                 Dim blockTable As BlockTable = Tx.GetObject(Active.Database.BlockTableId, OpenMode.ForRead, False, True)
  8.                 Dim btrId As ObjectId = Active.Database.Insert(tmpblkref.Name, blkDb, True)
  9.                 If btrId <> objectId.Null Then
  10.                     Dim btr As BlockTableRecord = Tx.GetObject(btrId, OpenMode.ForRead, False, True)
  11.                     Dim brefIds As ObjectIdCollection = btr.GetBlockReferenceIds(False, True)
  12.                     For Each id As ObjectId In brefIds
  13.                         Dim bref As BlockReference =
  14.                         Tx.GetObject(id, OpenMode.ForWrite, False, True)
  15.                         bref.RecordGraphicsModified(True)
  16.                     Next
  17.                 End If
  18.             Catch ex As Exception
  19.                 Tx.Commit()
  20.                 Tx.Dispose()
  21.                 blkDb.Dispose()
  22.             End Try
  23.         End Function

...

The code in "Try..." looks OK to me (if it is a simple block without Attribute, or the newly defined block definition has not attribute change, at least), until the code reaches the end of the Function (when the Transaction is out of scope), because the Transaction is not COMMITTED! You placed the Transaction.Commit()/Dispose() in "Catch..." clause, which is only executed when exception is raised in "Try...". Since the Transaction is never committed, it would be disposed outside your function at some stage, thus, your change in this function would be rolled back, I guess.

vegbruiser

  • Guest
Thanks n.yuan; that missed Commit() certainly won't have helped matters. Doh!

MexicanCustard

  • Swamp Rat
  • Posts: 705

Is it a case of doing the redefine as you said and then iterating through the attributecollection(s) of both old/new blockreferences, finding the matching attributedefinition.tag values (since they haven't changed!) and transferring the attributereference.textstring value from old to new?

or am I overthinking what needs to be done?

You're not over thinking it at all, thats exactly what you need to do.  Just iterate over the existing collection and assign values to the new collection.
Revit 2019, AMEP 2019 64bit Win 10

vegbruiser

  • Guest
Thanks again MC; I figured out a solution that did pretty much everything I listed (and you confirmed) tomorrow or over the (long) weekend I'll post what I have. :)