Everything aches after a very enjoyable soccer tournament in Prague (to which I mentioned I was heading off in my last post). But it was well worth the pain; it was really a lot of fun playing with and against (and just catching up with) so many friends and colleagues.
I received this question a week or so ago by email:
Is there any way to change the handle of an object through the API? Ideally I would like to select two objects and swap the handle.
The code in this post does just that: it asks the user to select two entities, and then swaps their identities (their Handles and ObjectIds), printing some "before & after" information to prove something actually happened.
A quick reminder... please feel free to send through interesting topics by email, but I must stress that these requests will be looked upon only as potential material for blog posts: if you need support related to a specific problem within a particular timeframe, please make use of the public discussion groups or submit your question via the ADN website, if you're a member. In case you're wondering why I'm not available to answer all your questions - something I often wish I could do - I should probably point out that I have a "day job" (managing the worldwide DevTech team) in addition to the time I spend blogging. There simply aren't enough hours in the day for me to do everything, however sad that makes me.
[By the way... if you're not an Autodesk Developer Network member, I do recommend joining if you have a strong need for API support: my team provides API support of a very high quality (something we measure through satisfaction surveys, etc.) and - assuming you value timely, quality support - this and the other program benefits should more than justify the cost. And ADN is not just for developers of commercial software: an increasing number of customers (and consultants implementing software specifically for customers) are joining the program.]
Anyway - back to the topic at hand... at the root of the below code is a single API call: DBObject.SwapIdWith(). Here's what the AutoCAD .NET documentation (found in the ObjectARX SDK, for now), says about the SwapIdWith() function:
This function swaps objectIds and handles between the object specified by otherId and the object invoking this function. Both objects must currently be database-resident and must reside in the same database. If swapExtendedData is true, then the objects swap extended entity data as well. If swapExtensionDictionary is true, then the objects swap extension dictionaries also.
So this function does exactly what was asked: all we then need to do is wrap it up with some code to select the entities and dump their identities to the command-line/text-screen.
Here's the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
namespace ObjectIdentity
{
public class Commands
{
[CommandMethod("SE")]
static public void SwapEntities()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
PromptEntityResult per =
ed.GetEntity("\nSelect first entity: ");
if (per.Status != PromptStatus.OK)
return;
ObjectId firstId = per.ObjectId;
per = ed.GetEntity("\nSelect second entity: ");
if (per.Status != PromptStatus.OK)
return;
ObjectId secondId = per.ObjectId;
Transaction tr =
doc.Database.TransactionManager.StartTransaction();
using (tr)
{
DBObject firstObj =
tr.GetObject(firstId, OpenMode.ForRead);
DBObject secondObj =
tr.GetObject(secondId, OpenMode.ForRead);
PrintIdentities(firstObj, secondObj);
PromptKeywordOptions pko =
new PromptKeywordOptions(
"\nSwap their identities?"
);
pko.AllowNone = true;
pko.Keywords.Add("Yes");
pko.Keywords.Add("No");
pko.Keywords.Default = "No";
PromptResult pkr =
ed.GetKeywords(pko);
if (pkr.StringResult == "Yes")
{
try
{
firstObj.UpgradeOpen();
firstObj.SwapIdWith(secondId, true, true);
PrintIdentities(firstObj, secondObj);
}
catch (Exception ex)
{
ed.WriteMessage(
"\nCould not swap identities: " + ex.Message
);
}
}
tr.Commit();
}
}
private static void PrintIdentities(
DBObject first, DBObject second)
{
PrintIdentity(first, "First");
PrintIdentity(second, "Second");
}
private static void PrintIdentity(
DBObject obj, string name)
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ed.WriteMessage(
"\n{0} object, of type {1}: " +
"ObjectId is {2}, " +
"Handle is {3}.",
name,
obj.GetType().Name,
obj.ObjectId,
obj.Handle
);
}
}
}
Here's what happens when we run the SE command, selecting a Line and a Circle that have been created in a fresh drawing:
Command: SE
Select first entity:
Select second entity:
First object, of type Line: ObjectId is (-1075825472), Handle is 178.
Second object, of type Circle: ObjectId is (-1075825464), Handle is 179.
Swap their identities? [Yes/No] <No>: Y
First object, of type Line: ObjectId is (-1075825464), Handle is 179.
Second object, of type Circle: ObjectId is (-1075825472), Handle is 178.
So you can see that the handles and object identifiers of the Circle and the Line have been swapped.
Another function you may find useful - if you care about maintaining object identity - is DBObject.HandOverTo(). This allows you to replace a Database-resident object with another, non-Database resident one, maintaining the identity (ObjectId and Handle) of the original. This is useful if you want to implement an operation that modifies an object but has an outcome that's of a different object type. For example, if you break a Circle, you may want to be left with an Arc which has the original Circle's identity. But we'll look at this type of scenario more closely in a future post.