Swapping identities of AutoCAD objects using .NET

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.

8 responses to “Swapping identities of AutoCAD objects using .NET”

  1. hi , is there way to view autocad files on web pages? (in asp.net using c#)?
    or in windows .net applications?
    plz help me , tnx

  2. Kean Walmsley Avatar

    You can embed DWG TrueView or publish to DWF from AutoCAD and use Design Review (both client-side controls). Or you can use Autodesk Freewheel if you want something zero-client.

    Kean

  3. is this going to work on civil 3d objects such as alignments?

  4. Hi Bob,

    It should work with any AutoCAD object (including those added by vertical applications), although I haven't specifically tested it with Civil 3D objects.

    There may also be things like inter-object references that you need to take into account, so I suggest proceeding with caution.

    Kean

  5. Hi Kean,

    Thanks for this blog.
    I am handling a case, and found out that SwapIdWith doesn't work well with ACA objects, when one of the entity is anchored with anchor.

    For example,
    First entity is a wall with a door.
    second entity is a wall without a door.
    after using the "SE" command in this blog, the first entity lose the door, and the door is inserted to the second wall.

    Wall and door are associated with the help of anchor. In this case, we need explicitly change the anchor's referencing wall.

    Joe Ye

  6. Kean Walmsley Avatar

    Thanks for the comment, Joe.

    You're right: care must be taken when swapping objects which reference or depend on other objects (beyond the standard layer/linetype etc.).

    It was never a "use case" for this code to work with anchored ACA objects, for instance, but perhaps I should have made that clear in the post itself.

    Regards,

    Kean

  7. Hi Kean,
    when I swap to linetypes (from the linetype table) then I have problems afterward with the table. It seems to me that i miss some kind of update of the table data. For example if I swap the linetypes "Demo" and "Demo2" then the method LtTable.Has("Demo2") returns true, as expected, however LtTable.Has("Demo") returns true before but false after the swapping. Any ideas? Thanks.
    Kind regards,
    Stephan

  8. Hi Stephan,

    I haven't swapped symbol table records in quite this way, but it doesn't seem right, somehow.

    Please post your question via ADN or to the AutoCAD .NET discussion group (as mentioned in this post).

    Thanks,

    Kean

Leave a Reply to Kean Walmsley Cancel reply

Your email address will not be published. Required fields are marked *