TheSwamp

Code Red => .NET => Topic started by: vegbruiser on April 14, 2014, 05:45:18 AM

Title: Definitive example of how to replace an existing blockreference with Attributes
Post by: vegbruiser on April 14, 2014, 05:45:18 AM
Hi Folks,

I've been looking here (http://docs.autodesk.com/ACD/2014/PTB/index.html?url=files/GUID-FB03D1E5-2C84-4078-92EF-9FB2D57CA1C5.htm,topicNumber=d30e722056), here (http://adndevblog.typepad.com/autocad/2012/05/redefining-a-block.html) and here (http://www.theswamp.org/index.php?topic=31859.0;all) at examples showing how to replace/redefine an existing blockdefinition, but my implementation so far (based on this (http://adndevblog.typepad.com/autocad/2012/05/redefining-a-block.html)) 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.
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: MexicanCustard on April 14, 2014, 08:24:05 AM
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.         }
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: vegbruiser on April 14, 2014, 08:47:54 AM
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?
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: Kerry on April 14, 2014, 09:05:36 AM
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
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: n.yuan on April 14, 2014, 09:40:26 AM
...
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.
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: vegbruiser on April 14, 2014, 11:08:04 AM
Thanks n.yuan; that missed Commit() certainly won't have helped matters. Doh!
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: MexicanCustard on April 16, 2014, 01:14:31 PM

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.
Title: Re: Definitive example of how to replace an existing blockreference with Attributes
Post by: vegbruiser on April 16, 2014, 06:31:46 PM
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. :)