Through the Interface: Using transient graphics to simulate AutoCADs MOVE command using .NET

Kean Walmsley

May 2015

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30


« Handling DPI scaling in your .NET application’s forms | Main | Adding ortho support to the transient graphics AutoCAD MOVE using .NET »

March 14, 2011

Using transient graphics to simulate AutoCAD’s MOVE command using .NET

A big thanks to Norman Yuan for the technique shown in this post. I stumbled across Norman’s post on this topic, and decided to take his code and rework it for posting here, with Norman’s permission, of course. Very interesting stuff! :-)

First things first: you probably don’t have a burning need to reinvent either the wheel or AutoCAD’s MOVE command. That said, this is a technique that may be of interest to many of you, especially if – for whatever reason – you’re looking for an alternative to using a jig. The technique Norman has shown uses a combination of a PointMonitor and AutoCAD’s transient graphics API (available since AutoCAD 2009), to effectively draw a set of entities transiently at the cursor as you move it across the screen.

From a purist’s perspective, I’d almost always use a jig for this – as in many ways this is the kind of low-level behaviour a DrawJig encapsulates, for instance – but then I’m also aware that there are always strange edge-cases where people want more control, or just want to do something downright strange. This technique is another tool in your box that may be of assistance in those situations, and is certainly not lacking in ingenuity.

My main changes to Norman’s original code were to remove the use of member state – which also meant shifting away from a member variable for the PointMonitor event handler to using a local delegate with access to variables in scope when the handler was added – as well as removing some manual techniques for selection & highlighting. If you’re interested, please do head across to Norman’s blog to check out his original implementation.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

using System;


namespace CustomMove


  public class Commands


    [CommandMethod("MYMOVE", CommandFlags.UsePickSet)]

    public static void CustomMoveCmd()


      Document doc =



      Editor ed = doc.Editor;


      // Start by getting the objects to move

      // (will use the pickfirst set, if defined)


      PromptSelectionResult psr = ed.GetSelection();

      if (psr.Status != PromptStatus.OK || psr.Value.Count == 0)



      // Create a collection of the selected objects' IDs


      ObjectIdCollection ids =

        new ObjectIdCollection(psr.Value.GetObjectIds());


      // Ask the user to select a base point for the move


      PromptPointResult ppr =

        ed.GetPoint("\nSpecify base point: ");

      if (ppr.Status != PromptStatus.OK)



      Point3d basePt = ppr.Value;

      Point3d curPt = basePt;


      // A local delegate for our event handler so

      // we can remove it at the end


      PointMonitorEventHandler handler = null;


      // Our transaction


      Transaction tr =


      using (tr)


        // Create our transient drawables, with associated

        // graphics, from the selected objects


        List<Drawable> drawables = CreateTransGraphics(tr, ids);



          // Add our point monitor

          // (as a delegate we have access to basePt and curPt,

          //  which avoids having to access global/member state)


          handler =

            delegate(object sender, PointMonitorEventArgs e)


              // Get the point, with "ortho" applied, if needed


              Point3d pt = e.Context.RawPoint;

              if (IsOrthModeOn())

                pt = GetOrthoPoint(basePt, pt);


              // Update our graphics and the current point


              UpdateTransGraphics(drawables, curPt, pt);

              curPt = pt;



          ed.PointMonitor += handler;


          // Ask for the destination, during which the point

          // monitor will be updating the transient graphics


          PromptPointOptions opt =

            new PromptPointOptions("\nSpecify second point: ");

          opt.UseBasePoint = true;

          opt.BasePoint = basePt;

          ppr = ed.GetPoint(opt);


          // If the point was selected successfully...


          if (ppr.Status == PromptStatus.OK)


            // ... move the entities to their destination



              tr, basePt,

              IsOrthModeOn() ?

                GetOrthoPoint(basePt, ppr.Value) :





            // And inform the user



              "\n{0} object{1} moved", ids.Count,

              ids.Count == 1 ? "" : "s"




        catch (Autodesk.AutoCAD.Runtime.Exception ex)


          ed.WriteMessage("\nException: {0}", ex.Message);




          // Clear any transient graphics




          // Remove the event handler


          if (handler != null)

            ed.PointMonitor -= handler;








    private static bool IsOrthModeOn()


      // Check the value of the ORTHOMODE sysvar


      object orth =




      return Convert.ToInt32(orth) > 0;



    private static Point3d GetOrthoPoint(

      Point3d basePt, Point3d pt



      // Apply a crude orthographic mode


      double x = pt.X;

      double y = pt.Y;


      Vector3d vec = basePt.GetVectorTo(pt);

      if (Math.Abs(vec.X) >= Math.Abs(vec.Y))

        y = basePt.Y;


        x = basePt.X;


      return new Point3d(x, y, 0.0);



    private static void MoveEntities(

      Transaction tr, Point3d basePt, Point3d moveTo,

      ObjectIdCollection ids



      // Transform a set of entities to a new location


      Matrix3d mat =


      foreach (ObjectId id in ids)


        Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite);





    private static List<Drawable> CreateTransGraphics(

      Transaction tr, ObjectIdCollection ids



      // Create our list of drawables to return


      List<Drawable> drawables = new List<Drawable>();


      foreach (ObjectId id in ids)


        // Read each entity


        Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);


        // Clone it, make it red & add the clone to the list


        Entity drawable = ent.Clone() as Entity;

        drawable.ColorIndex = 1;




      // Draw each one initially


      foreach (Drawable d in drawables)



          d, TransientDrawingMode.DirectShortTerm,

          128, new IntegerCollection()



      return drawables;



    private static void UpdateTransGraphics(

      List<Drawable> drawables, Point3d curPt, Point3d moveToPt



      // Displace each of our drawables


      Matrix3d mat =



      // Update their graphics


      foreach (Drawable d in drawables)


        Entity e = d as Entity;




          d, new IntegerCollection()





    private static void ClearTransGraphics(

      List<Drawable> drawables



      // Clear the transient graphics for our drawables




        128, new IntegerCollection()



      // Dispose of them and clear the list


      foreach (Drawable d in drawables)








When you run the MYMOVE command, selecting some geometry to move, you’ll be prompted for a base point and destination:

Custom move command in action

You should see the selected geometry in red – unless you’re moving complex such as block references, in which case it depends on whether the contents are picking up the colour of the reference, or not – until you cancel the command or select the destination.

One issue that I deliberately chose not to address, for now, is that of ORTHOMODE support: the above implementation provides fairly crude orthographic drawing support, but I did notice more work is needed for it to work properly in 3D with a non-World UCS. This is probably something I’ll look at addressing in a follow-up post (if Norman doesn’t end up posting a fix here in the meantime :-).

Thanks again, Norman, for this interesting technique!

blog comments powered by Disqus


10 Random Posts