It appears that the copied and/or dynamic blocks (DB's for short) visibility states are not recognized by a block replace routine in a otherwise powerful AutoCAD add-on called ToolPac by www.dotsoft.com.
As a fledgling VB.NET programmer, I would like to find out what would be needed, in the form of general comments or code snippet form, to handle those annoying anonymous blocks (and/or DB's visibility states)? :x
I wouldn't want to ruin Mr. Dotson's business, but if his block replacement tool works how I imagine it to work, there's very little complexity to it except in the very special case of dynamic block references.
Now, if I understand you correctly, you want to preserve the values of your dynamic properties when switching definitions for a given set of block references, otherwise you wouldn't worry about those anonymous blocks. As far as I can ascertain, that's not quite trivial, because we would have to ensure that the property on a reference with the old definition will be supported under the new definition too. It's not sufficient to merely read the dynamic properties, store them in some container, and write them back after changing the definition. An example for this way to copy properties can be found
here.
Now we have to read the property descriptors from the BlockTableRecord as well. There may be a better way but this function will do:
Function PropDescDictFromBtr(ByRef btr As BlockTableRecord, ByRef tr As Transaction) _
As Dictionary(Of String, BlockParameterPropertyDescriptor)
Dim pddict = New Dictionary(Of String, BlockParameterPropertyDescriptor)
Dim exdict = CType(tr.GetObject(btr.ExtensionDictionary, OpenMode.ForRead), DBDictionary)
If exdict.Contains("ACAD_ENHANCEDBLOCK") Then
Dim eg = CType(tr.GetObject(exdict.GetAt("ACAD_ENHANCEDBLOCK"), OpenMode.ForRead), EvalGraph)
For Each i In eg.GetAllNodes()
Dim bp = TryCast(eg.GetNode(CUInt(i), OpenMode.ForRead, tr), BlockParameter)
If bp <> Nothing Then
For Each pd As BlockParameterPropertyDescriptor In bp.PropertyDescription
If Not pddict.ContainsKey(pd.PropertyName) Then
pddict.Add(pd.PropertyName, pd)
End If
Next
End If
Next
End If
Return pddict
End Function
The command sub loops simply through a SelectionSet and tries to combine those two approaches outlined above:
<CommandMethod("BlockReplace", CommandFlags.UsePickSet)> _
Public Sub DynPropBlockReplace()
Dim dm = Application.DocumentManager
Dim ed = dm.MdiActiveDocument.Editor
Dim db = dm.MdiActiveDocument.Database
Dim psr = ed.GetSelection(New SelectionFilter(New TypedValue() {New TypedValue(DxfCode.Start, "INSERT")}))
If psr.Status <> PromptStatus.OK OrElse psr.Value.Count <= 0 Then Return
Dim pr = ed.GetString(New PromptStringOptions(Microsoft.VisualBasic.vbCrLf +
"Enter block name: ") With {.AllowSpaces = True})
If pr.Status <> PromptStatus.OK OrElse pr.StringResult = "" Then Return
Using tr = db.TransactionManager.StartTransaction()
Dim bt = CType(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
If Not bt.Has(pr.StringResult) Then
ed.WriteMessage(Microsoft.VisualBasic.vbCrLf +
"Block name ""{0}"" not defined ", pr.StringResult)
Else
Dim btr = CType(tr.GetObject(bt(pr.StringResult), OpenMode.ForRead), BlockTableRecord)
If Not btr.IsDynamicBlock Then
For Each id In psr.Value.GetObjectIds()
CType(tr.GetObject(id, OpenMode.ForWrite), BlockReference).BlockTableRecord = bt(pr.StringResult)
Next
Else
Dim pddict = PropDescDictFromBtr(btr, tr)
For Each id In psr.Value.GetObjectIds()
Dim br = CType(tr.GetObject(id, OpenMode.ForWrite), BlockReference)
Dim dict = New Dictionary(Of String, DynamicBlockReferenceProperty)()
If br.IsDynamicBlock Then
For Each prop As DynamicBlockReferenceProperty In br.DynamicBlockReferencePropertyCollection
If Not prop.ReadOnly AndAlso Not dict.ContainsKey(prop.PropertyName) Then
dict.Add(prop.PropertyName, prop)
End If
Next
End If
br.BlockTableRecord = bt(pr.StringResult)
If br.IsDynamicBlock Then
For Each prop As DynamicBlockReferenceProperty In br.DynamicBlockReferencePropertyCollection
If Not prop.ReadOnly AndAlso
dict.ContainsKey(prop.PropertyName) AndAlso
prop.PropertyTypeCode = dict(prop.PropertyName).PropertyTypeCode AndAlso
pddict.ContainsKey(prop.PropertyName) AndAlso
prop.PropertyTypeCode = pddict(prop.PropertyName).PropertyType Then
prop.Value = dict(prop.PropertyName).Value
End If
Next
End If
Next
btr.UpdateAnonymousBlocks()
End If
End If
tr.Commit()
End Using
End Sub
Note that you will need a reference to the
Autodesk.AutoCAD.Internal.DatabaseServices namespace.
Have fun!