Through the Interface: Moving text in an AutoCAD block using .NET Part 1

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


« First impressions of Kinect for Windows 2.0 pre-release | Main | Moving text in an AutoCAD block using .NET – Part 2 »

December 02, 2013

Moving text in an AutoCAD block using .NET – Part 1

I had this question come in from Bruce Gordon by email a couple of weeks ago, and it seemed like a fun one to look at:

Is it possible to write a utility to pick a text entity in a block reference and move it to a new location?

As a first step, I put together some code that launches a nested entity selection and then performs a translation of the selected entity by a specified displacement from the picked point.

Given the requirement in the original request, the code works “first time” for text entities (DBText and MText), but asks the user for confirmation if a non-text entity is selected. As the displacement is performed via a standard matrix transformation – and every Entity has its TransformBy() method – this is a somewhat artificial distinction and can be removed very easily: it works very well with non-text too, of course. The transform needs a little extra work to work with nested (and rotated) blocks, in particular: we need to aggregate the block transforms of the containing block references and use those to get transformation into the equivalent of world coordinates. We can do this by pre- and post-multiplying the actual displacement matrix with the appropriate transforms. Comments in the code should help explain the specifics.

This type of command – one that modifies a shared definition that might be instanced multiple times (which is certainly the case with block definitions and their references) – always has the risk of causing user confusion. If you’re going to implement this in the wild then I suggest doing such things as checking the number of references to the block definitions you’re modifying so that the user can be informed of changes that might otherwise lead to head-scratching.

This version of the code doesn’t display the entity being dragged across – we do a standard GetPoint() having set the base point to get rubber-banding of the point selection, only – but we’ll add a jig in Part 2 that shows the entity being moved.

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;


namespace Textformations


  public class Commands



    public static void MoveTextInBlock()


      var doc = Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;


      // Start by getting the text (or other) object in the block


      var pneo =

        new PromptNestedEntityOptions("\nSelect text inside block");

      var pner = ed.GetNestedEntity(pneo);


      if (pner.Status != PromptStatus.OK)



      var selId = pner.ObjectId;


      // Start a transaction to access the object


      var oc = selId.ObjectClass;

      if (



        ) &&






        // Isn't a text object - ask whether we continue



          "\nObject is not text, it is a {0}.", oc.Name



        var pko =

          new PromptKeywordOptions(

            "\nDo you want to continue? [Yes/No]", "Yes No"


        pko.AppendKeywordsToMessage = true;

        pko.AllowNone = true;

        pko.Keywords.Default = "No";


        var pr = ed.GetKeywords(pko);

        if (

          pr.Status != PromptStatus.OK || pr.StringResult == "No"





      // Now get the displacement from the picked point


      var ppo = new PromptPointOptions("\nEnter displacement");

      ppo.BasePoint = pner.PickedPoint;

      ppo.UseBasePoint = true;


      var ppr = ed.GetPoint(ppo);

      if (ppr.Status != PromptStatus.OK)



      // Start a transaction to modify the object


      using (var tr = doc.TransactionManager.StartTransaction())


        // Get the displacement from the picked point to the newly

        // selected one


        var disp = ppr.Value - pner.PickedPoint;

        var mat = Matrix3d.Displacement(disp);


        // Unless we get a block reference container, use the

        // identity matrix as the block transform


        var brMat = Matrix3d.Identity;


        // Get the containers around the nested entity


        var conts = pner.GetContainers();

        foreach (var id in conts)


          var br =

            tr.GetObject(id, OpenMode.ForRead) as BlockReference;

          if (br != null)


            brMat = brMat.PreMultiplyBy(br.BlockTransform);




        // To get the transformation in world coordinates rather

        // than block coordinates we post-multiply by block

        // transform and pre-multiply by its inverse


        mat = mat.PreMultiplyBy(brMat.Inverse());

        mat = mat.PostMultiplyBy(brMat);


        // Transform the entity


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



        // Open each of the containers and set a property so that

        // they each get regenerated


        foreach (var id in conts)


          var ent2 = tr.GetObject(id, OpenMode.ForWrite) as Entity;

          if (ent2 != null)


            // We might also have called this method:

            // ent2.RecordGraphicsModified(true);

            // but setting a property works better with undo


            ent2.Visible = ent2.Visible;









Here’s a quick video of the command in action:


Thanks to Bruce Gordon for asking an interesting question and then helping test this implementation. It’s certainly an interesting technique that can be used in lots of different scenarios.

I’m now at AU2013 and will probably post about the conference over the coming days, so we’ll see when I get to post the follow-up post that adds preview graphics via a jig.

blog comments powered by Disqus


10 Random Posts