I need to Redefine Dynamic blocks and update all existing instances of the original block within a drawing to conform to the new definition. The new block definition will be read from a different dwg file/database.
To accomplish this in ACAD, I run Insert, select the file containing the updated block definition (with block name matching that of existing blocks to be redefined). When blocks with the same name are present in the active drawing, I am prompted to redefine and all existing blocks are updated.
I found code that seemes to meant to accomplish this exact bit of work on the AutoCAD DevBlock (http://adndevblog.typepad.com/autocad/2012/05/redefining-a-block.html). I then rewrote this code which I will provide.
When I run the method, whether it is the original code provided on the blog or my own, the new block definition is written to the database successfully. However, no existing block references are found after, so the code meant to update the geometry of existing references is never executed. The exising block instances do not reflect the changes made to the block definition after this code runs.
Oddly, opening an existing block instance in the block definition editor after running this code makes it clear that the new block defintion is in place.
Here is my code:
Public Shared Function TryRedifineBlock(BlockName As String, BlockFilePath As String) As Boolean Dim result As Boolean = False Dim doc As Document = ApplicationServices.Application.DocumentManager.MdiActiveDocument Using lock As DocumentLock = doc.LockDocument() Using tran As Transaction = doc.TransactionManager.StartTransaction() Try Dim blockDB As Database = New Database(False, True) blockDB.ReadDwgFile(BlockFilePath, FileOpenMode.OpenForReadAndReadShare, True, "") Dim blockTableRecordID As ObjectId = doc.Database.Insert(BlockName, blockDB, True) If Not blockTableRecordID.IsNull Then Dim btr As BlockTableRecord = tran.GetObject(blockTableRecordID, OpenMode.ForRead, False, True) For Each bRefID As ObjectId In btr.GetBlockReferenceIds(False, True) Dim bRef As BlockReference = tran.GetObject(bRefID, OpenMode.ForWrite, False, True) bRef.RecordGraphicsModified(True) Next End If tran.Commit() blockDB.Dispose() result = True Catch ex As System.Exception tran.Abort() result = False Try EventLog.WriteEntry("DynamicBlockHelper.TryRedifineBlock", ex.GetType().Name + ": " + ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error) Catch eex As Exception End Try End Try End Using End Using Return result End Function
I have also attached the definition of the dynamic block I am using for testing. I apologize in advance if my ACAD termonology is off. I am fairly new to ACAD and its APIs.
Best Regards,
Kevin
Solved! Go to Solution.
Solved by norman.yuan. Go to Solution.
Did you debug your code by stepping through this part of code:
If Not blockTableRecordID.IsNull Then
Dim btr As BlockTableRecord = tran.GetObject(blockTableRecordID, OpenMode.ForRead, False, True)
For Each bRefID As ObjectId In btr.GetBlockReferenceIds(False, True)
Dim bRef As BlockReference = tran.GetObject(bRefID, OpenMode.ForWrite, False, True)
bRef.RecordGraphicsModified(True)
Next
End If
If you place a breakpoint inside the "For Each..." loop, you should be able to tell if there is any BlockRefernce is updated or not. In your case, since the block is a DYNAMIC BLOCK, if all BlockReference instances have their dynamic property/properties set to different value(s) from their original value defined in block definition, these BlockReferences will actually be the direct reference to dynamically created anonymous BlockTableRecord. You need to use BlockTableRecord.GetAnonymousBlockIds() to find out all the anonymous blockTableRecords that is derived from the dynamic BlockTableRecord, then find all the BlockReferences of all these anonymous BlockTableRecord.
See a very resent discussion very similar to your case:
http://forums.autodesk.com/t5/NET/How-to-iterate-through-block-values/m-p/4925154
Norman Yuan
Norman,
Thank you for the reply. It is truely appreciated. I revised my code based on your advice:
Public Shared Function TryRedifineBlock(BlockName As String, BlockFilePath As String) As Boolean Dim result As Boolean = False Dim doc As Document = ApplicationServices.Application.DocumentManager.MdiActiveDocument Using lock As DocumentLock = doc.LockDocument() Using tran As Transaction = doc.TransactionManager.StartTransaction() Try Dim blockDB As Database = New Database(False, True) blockDB.ReadDwgFile(BlockFilePath, FileOpenMode.OpenForReadAndReadShare, True, "") Dim blockTableRecordID As ObjectId = doc.Database.Insert(BlockName, blockDB, True) If Not blockTableRecordID.IsNull Then Dim btr As BlockTableRecord = tran.GetObject(blockTableRecordID, OpenMode.ForRead, False, True) For Each bRefID As ObjectId In btr.GetBlockReferenceIds(False, True) Dim bRef As BlockReference = tran.GetObject(bRefID, OpenMode.ForWrite, False, True) bRef.RecordGraphicsModified(True) Next If btr.IsDynamicBlock Then For Each dynBtr As ObjectId In btr.GetAnonymousBlockIds() Dim dynBr As BlockTableRecord = DirectCast(tran.GetObject(dynBtr, OpenMode.ForRead), BlockTableRecord) For Each dynBlkID As ObjectId In dynBr.GetBlockReferenceIds(True, True) Dim dynBlkRef As BlockReference = tran.GetObject(dynBlkID, OpenMode.ForWrite, False, True) dynBlkRef.RecordGraphicsModified(True) Next Next End If End If tran.Commit() blockDB.Dispose() result = True Catch ex As System.Exception tran.Abort() result = False Try EventLog.WriteEntry("DynamicBlockHelper.TryRedifineBlock", ex.GetType().Name + ": " + ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error) Catch eex As System.Exception End Try End Try End Using End Using Return result End Function
Now block reference IDs are indeed found. However, the new block definition is not applied to the "dynamically created block definitions", so the call to RecordGraphicsModified has not effect and the method still does not work as intended.
How would I go about updating these dynamically generated block definitions with the updated definitition from the secondary database? Thank you in advance.
Best Regards,
Kevin
I have never tried to write code to update a dynamic block definition and then update all the BlockReferences myself. So, I am not aware that there is simpler way to do it than re-inserting the dynamic block reference. That is, assuming the re-defined block definition has the same dynamic properties and all the dynamic properties would be set to the same value:
1. Find each Dynamic BlockReference;
2. read all dynamic properties' value and save them in variable(s);
3. erase the existing dynamic BlockReference;
4. Insert a new BlockReference, based in the newly updated dynamic BlockTableRecord, with the same position/scale/rotate/attributes...;
5. Set dynamic properties' value of the newly inserted dynamic BlockReference;
Norman Yuan
I considered this solution, but I had hoped to find an alternative. Perhaps what you suggest is the best solution, since it will allow me complete control over copying Block Property states and Attribute states.
Still, I can acheive my aim by running the insert command in autocad, selecting the new block defintion, then redefining when prompted to.
I did find a post (http://forums.autodesk.com/t5/Dynamic-Blocks/Redefining-Updating-Dynamic-Blocks/td-p/1623916/highlig... in the Dynamic Block section of the forums which suggests a course of action which might be achieved using .net:
1) RENAME block to be updated; 2) Insert new block; 3) use BLOCKREPLACE to
update renamed block; 4) Use ATTSYNC if necessary.
However, I think I will implement the method you suggest because it offers me more control and is very likely to work as expect.
Thank you very much for you insights. I will post my code after it is tested.
Best Regards,
Kevin
I use norman's 5 steps to update Dynamic BlockReferences after I changed the BlockDefinition.
Be aware of the Dynamic Property "Origin", which appears in a Dynamic Block when a "Length" parameter and/or a "Stretch" action is added.
Reading its value (always (0,0,0)?) and setting it again will mess up all your anonymous References.
You have to call updateAnonymousBlocks() after you set the saved dynamic properties to the replaced dynamic block, e.g.
AcDbDynBlockReference mm_dynBlockReference (blockReference);
bool mm_isDynamic = mm_dynBlockReference.isDynamicBlock();
AcDbDynBlockReferencePropertyArray mm_dynamicProperties;
if (mm_isDynamic)
{
mm_dynBlockReference.getBlockProperties(mm_dynamicProperties);
for (int mm_index = 0; mm_index < mm_dynamicProperties.length(); mm_index++)
{
enString mm_propertyName = mm_dynamicProperties[mm_index].propertyName();
MapPropertyNameToValue::const_iterator mm_iter = mapValues.find(mm_propertyName);
if (mm_iter != mapValues.end())
{
mm_dynamicProperties[mm_index].setValue(mm_iter->second);
}
}
AcDbObjectId dynBlockDefinitionId = mm_dynBlockReference.dynamicBlockTableRecord();
if (!dynBlockDefinitionId.isNull())
{
AcDbDynBlockTableRecord dynamicBlockDefinition(dynBlockDefinitionId);
dynamicBlockDefinition.updateAnonymousBlocks();
}
There should be a similar method in .net.
You're replying to a 3 year-old post, in case you didn't notice.
@u.steinmetz wrote:You have to call updateAnonymousBlocks() after you set the saved dynamic properties to the replaced dynamic block, e.g.