A big thanks to Philippe Leefsma, from the DevTech team in Europe, for providing this handy piece of code in a response to an ADN member.
In the last post we saw some code that made use of DBObject.HandOverTo() to maintain identity between an old line and a new one with which we wanted to replace it. This code makes use of this method’s counterpart, DBObject.SwapIdWith(), which works on two database-resident objects (rather than the replacement being in-memory, as is the case with HandOverTo()).
Here’s C# code implementing two commands, to swap the identity (and therefore the order) of two attribute definitions and of two attribute references, respectively:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
namespace BlockAttributeSwapping
{
public class Commands
{
[CommandMethod("SAD")]
static public void SwapAttributeDefinitions()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// Select a block reference
PromptEntityOptions peo =
new PromptEntityOptions("\nSelect a block reference:");
peo.SetRejectMessage("\nMust be block reference...");
peo.AddAllowedClass(typeof(BlockReference), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// Open the block reference and its connected BTR
BlockReference br =
(BlockReference)tr.GetObject(
per.ObjectId,
OpenMode.ForRead
);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
br.BlockTableRecord,
OpenMode.ForRead
);
// If the block definition has attribute definitions...
if (btr.HasAttributeDefinitions)
{
// We want to find the first two
ObjectId attId1 = ObjectId.Null;
ObjectId attId2 = ObjectId.Null;
// Loop through and get them
// (We could clearly extend this to work differently,
// picking up attributes by name.)
foreach (ObjectId id in btr)
{
DBObject obj = tr.GetObject(id, OpenMode.ForRead);
if (obj is AttributeDefinition)
{
if (attId1 == ObjectId.Null)
attId1 = id;
else
{
attId2 = id;
break;
}
}
}
// If we have to attribute definitions...
if (attId1 != ObjectId.Null && attId2 != ObjectId.Null)
{
// Open the first and swap it with the second
AttributeDefinition ad =
(AttributeDefinition)tr.GetObject(
attId1,
OpenMode.ForWrite
);
ad.SwapIdWith(attId2, true, true);
ed.WriteMessage(
"\nOrder of first two attribute definitions swapped."
);
}
// Don't forget to commit the transaction
tr.Commit();
}
}
}
[CommandMethod("SAR")]
static public void SwapAttributeReferences()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// Select a block reference
PromptEntityOptions peo =
new PromptEntityOptions("\nSelect a block reference:");
peo.SetRejectMessage("\nMust be block reference...");
peo.AddAllowedClass(typeof(BlockReference), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
Transaction tr =
db.TransactionManager.StartTransaction();
using (tr)
{
// This time we just open the block reference
BlockReference br =
(BlockReference)tr.GetObject(
per.ObjectId,
OpenMode.ForRead
);
// If the block reference has attribute references...
if (br.AttributeCollection.Count > 1)
{
// Once again we just want the first two
// (We could clearly extend this to work differently,
// picking up attributes by name.)
ObjectId attId1 = br.AttributeCollection[0];
ObjectId attId2 = br.AttributeCollection[1];
// Open the first and swap it with the second
AttributeReference ar =
(AttributeReference)tr.GetObject(
attId1,
OpenMode.ForWrite
);
ar.SwapIdWith(attId2, true, true);
ed.WriteMessage(
"\nOrder of first two attribute references swapped."
);
}
// Don't forget to commit the transaction
tr.Commit();
}
}
}
}
Here’s what happens when we run the commands with a simple block containing two attributes:
When we edit the attributes by double-clicking one of them in the block, we see the initial order, as expected:
If we then swap the order of the attribute references using the SAR command, we see the order reversed when we next edit the attributes:
The position of the attributes in the drawing is not affected, however, so don’t expect that to change using either of the commands defined in this post.
To see the effect of swapping the order of the attribute definitions in the block table record, start by inserting the block, then run the SAD command and insert the block again:
Command: INSERT
Specify insertion point or [Basepoint/Scale/X/Y/Z/Rotate]:
Enter attribute values
First value <First>:
Second value <Second>:
Command: SAD
Order of first two attribute definitions swapped.
Select a block reference:
Command: INSERT
Specify insertion point or [Basepoint/Scale/X/Y/Z/Rotate]:
Enter attribute values
Second value <Second>:
First value <First>:
You see the order of the attribute definitions has now been swapped, too.
This technique could clearly be extended to swap different attributes – perhaps those selected by name – but at least this code demonstrates the overall approach for swapping attribute order in AutoCAD.