In the last post we looked at some code that essentially dumped out the dynamic properties of dynamic blocks to the command-line. In this post we take it a step further and capture the properties from one dynamic block reference and attempt to apply them to another.
We're going to apply slightly different logic, depending on the situation...
If the block references refer to the same dynamic block definition (i.e. their DynamicBlockTableRecord property contains the same ObjectId) then we'll assume they have the same dynamic properties in the same order. So we'll go through and attempt to copy the property value from the first to the second. This may be a flawed approach, I don't know enough to say whether there are situations where dynamic blocks might have different sets of properties or properties in a different order... if it proves to be a problem, it'd be a simple matter to remove this clause and rely on the second approach for all dynamic block references.
If the block references refer to different dynamic block definitions (i.e. their DynamicBlockTableRecord property contains different ObjectIds) then we use a slightly more elaborate approach: we go through the dynamic properties of the "source" dynamic block reference and store them in a dictionary. We then go through the dynamic properties of the "target" dynamic block reference, and if we find a value in the list we then attempt to apply it to this reference. In this way we should be able to paint properties across blocks - as long as they have the same name - even if they don't have the same overall make-up.
Here's the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;
namespace DynamicBlocks2
{
public class Commands
{
[CommandMethod("ADBP")]
static public void ApplyDynamicBlockProps()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// Select the source and target block references
PromptEntityOptions peo =
new PromptEntityOptions(
"\nSelect source dynamic block reference: "
);
peo.SetRejectMessage("\nEntity is not a block.");
peo.AddAllowedClass(typeof(BlockReference), false);
PromptEntityResult per =
ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
ObjectId sourceId = per.ObjectId;
peo.Message =
"\nSelect target dynamic block reference: ";
per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
ObjectId targetId = per.ObjectId;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// Open our block references: the source (br1)
// for read and the target (br2) for write
BlockReference br1 =
(BlockReference)tr.GetObject(
sourceId,
OpenMode.ForRead
);
BlockReference br2 =
(BlockReference)tr.GetObject(
targetId,
OpenMode.ForWrite
);
// They must both be dynamic block references
if (br1 != null && br1.IsDynamicBlock &&
br2 != null && br2.IsDynamicBlock)
{
if (br1.DynamicBlockTableRecord ==
br2.DynamicBlockTableRecord)
{
// They use the same block definition - let's assume
// the properties are in the same order
DynamicBlockReferencePropertyCollection pc1 =
br1.DynamicBlockReferencePropertyCollection;
DynamicBlockReferencePropertyCollection pc2 =
br2.DynamicBlockReferencePropertyCollection;
if (pc1.Count == pc2.Count)
{
for (int i = 0; i < pc1.Count; i++)
{
// Get each property. If they have the same
// name and are not read-only, attempt to
// copy the value from the source (prop1)
// to the target (prop2)
DynamicBlockReferenceProperty prop1 = pc1[i],
prop2 = pc2[i];
if (prop1.PropertyName == prop2.PropertyName &&
!prop1.ReadOnly && !prop2.ReadOnly)
{
prop2.Value = prop1.Value;
}
}
}
}
else
{
// If the block references refer to different
// dynamic block definitions, let's collect the
// properties for the first in a dictionary and
// attempt to apply them to the second
DynamicBlockReferencePropertyCollection pc1 =
br1.DynamicBlockReferencePropertyCollection;
// Create and populate our dictionary
Dictionary<string, DynamicBlockReferenceProperty> dict =
new
Dictionary<string, DynamicBlockReferenceProperty>();
foreach (DynamicBlockReferenceProperty prop in pc1)
{
if (!prop.ReadOnly &&
!dict.ContainsKey(prop.PropertyName))
dict.Add(prop.PropertyName, prop);
}
// Try to copy them to the target block reference's
// dynamic properties
DynamicBlockReferencePropertyCollection pc2 =
br2.DynamicBlockReferencePropertyCollection;
foreach (DynamicBlockReferenceProperty prop2 in pc2)
{
if (!prop2.ReadOnly &&
dict.ContainsKey(prop2.PropertyName))
{
try
{
DynamicBlockReferenceProperty prop1;
if (dict.TryGetValue(
prop2.PropertyName, out prop1
)
)
{
if (prop2.PropertyTypeCode ==
prop1.PropertyTypeCode)
{
prop2.Value = prop1.Value;
}
}
}
// Expand if you want to diagnose specific issues
catch { }
}
}
}
}
else
{
ed.WriteMessage(
"\nYou need to select two dynamic bock references."
);
}
tr.Commit();
}
}
}
}
Let's see what happens when we put our ADBP command through its paces. Here's our initial state - we have two differently-sized instances of the same dynamic block, plus one instance of a different dynamic block (which shares parameters - the left-hand block is a "Hex Socket Bolt (Side)" while the right-hand block is a "Hex Nut" - both these blocks are available in the "Mechanical - Metric.dwg" sample file in the Samples\Dynamic Blocks folder of your AutoCAD installation).
Here's where we are when we run our ADBP command and select the block reference at the top followed by the block reference at the bottom-left:
So we see it works on references of the same dynamic block definition. Now let's try it, selecting one of the left-hand elevation views on the bolt, followed by the plan view of the nut on the right:
Because they share a dynamic property ("Size", which went from "M10" to "M14"), we were able to "paint" from one to the other.
With AutoCAD 2010 you'll be able to create constraints between geometry - which is a much simpler and more powerful approach - but what we've seen in this post should be of use to people working with dynamic blocks today.
By the way - for those of you with ADN website access - I used this DevNote as inspiration for how to modify properties in a dynamic block reference: How to programmatically insert a dynamic block with attributes containing dynamic parameters? (requires login.) Thanks to Fernando for pointing me to it (at that point in my research I had not quite gotten as far as checking the ADN website... ahem), as well as for suggesting this topic in the first place.