October 2014

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 31  










« Changing the layer or color of a nested entity using .NET | Main | Importing the output from Photofly via the Photo Scene Editor into AutoCAD »

August 09, 2010

Changing the layer or color of a nested entity using .NET (take 2)

A big thanks to Thorsten Meinecke for pointing out the obvious issue with my prior post (the nested entity selection loop which has frustrated me unnecessarily when developing the code for that post and another).

Here are the details of the fix: using Editor.GetNestedEntity() along with a PromptNestedEntityOptions object, instead of a direct message string, allows us to specify AllowNone to be true. Which, in turn, causes PromptStatus.None to be returned when the enter or space keys are used to terminate the selection loop.

An obvious omission, in hindsight, but anyway – I’m much obliged to you, Thorsten! :-)

I’ve gone and updated the code – although I haven’t marked the individual line changes, as I’m supposed to be on vacation (yes, again, although in fairness I only had one week off of the three spent in San Francisco ;-) - which you will find below. The only other real change was to call UnhighlightSubEntities() even when the selection process is cancelled, as not doing so seems to leave selection graphics in an unstable state. I also took the opportunity to maintain flags to indicate whether a REGEN is really required, or not. So if either command is cancelled, no REGEN is performed.

Here’s the updated C# code:

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

 

namespace NestedObjectModification

{

  public class Commands

  {

    [CommandMethod("CNL")]

    static public void ChangeNestedLayer()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Collection of our selected entities and their sub-ent paths

 

      ObjectIdCollection ids;

      List<FullSubentityPath> paths;

 

      // Flag for whether a REGEN will be required

 

      bool regenRequired = false;

 

      // Start a transaction... will initially be used

      // to highlight the selected entities and then to

      // modify their layer

 

      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        if (SelectNestedEntities(ed, out ids, out paths) &&

            ids.Count > 0)

        {

          // Get the name of our destination later

 

          PromptResult pr =

            ed.GetString("\nNew layer for these objects: ");

          if (pr.Status == PromptStatus.OK)

          {

            // Check that the layer exists

 

            string newLay = pr.StringResult;

 

            LayerTable lt =

              tr.GetObject(db.LayerTableId, OpenMode.ForRead)

                as LayerTable;

 

            if (lt.Has(newLay))

            {

              // If so, set the layer name to be the one chosen

              // on each of the selected entitires

 

              for (int i = 0; i < ids.Count; i++)

              {

                Entity ent =

                  tr.GetObject(ids[i], OpenMode.ForWrite) as Entity;

                if (ent != null)

                {

                  regenRequired = true;

                  ent.Layer = newLay;

                }

              }

            }

            else

            {

              ed.WriteMessage(

                "\nLayer not found in current drawing."

              );

            }

          }

        }

        UnhighlightSubEntities(paths);

        tr.Commit();

 

        // Regen reflects the new layer

 

        if (regenRequired)

          ed.Regen();

      }

    }

 

    [CommandMethod("CNC")]

    static public void ChangeNestedColor()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Collection of our selected entities and their sub-ent paths

 

      ObjectIdCollection ids;

      List<FullSubentityPath> paths;

 

      // Flag for whether a REGEN will be required

 

      bool regenRequired = false;

 

      // Start a transaction... will initially be used

      // to highlight the selected entities and then to

      // modify their color

 

      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        if (SelectNestedEntities(ed, out ids, out paths) &&

            ids.Count > 0)

        {

          // Get the new color index

 

          PromptIntegerOptions pio =

            new PromptIntegerOptions(

              "\nNew color index for these objects: "

            );

          pio.AllowZero = true;

          pio.AllowNegative = false;

          pio.LowerLimit = 0;

          pio.UpperLimit = 256;

 

          PromptIntegerResult pir =

            ed.GetInteger(pio);

          if (pir.Status == PromptStatus.OK)

          {

            // If so, set the layer name to be the one chosen

            // on each of the selected entitires

 

            for (int i = 0; i < ids.Count; i++)

            {

              Entity ent =

                tr.GetObject(ids[i], OpenMode.ForWrite) as Entity;

              if (ent != null)

              {

                regenRequired = true;

                ent.ColorIndex = pir.Value;

              }

            }

          }

        }

        UnhighlightSubEntities(paths);

        tr.Commit();

      }

 

      // Regen reflects the new color

 

      if (regenRequired)

        ed.Regen();

    }

 

    // Ask the user to select a number of sub-entities.

    // These will have their ObjectIds and their sub-entity

    // paths (returned from the HighlightSubEntity() helper)

    // added to collections that are returned to the caller.

 

    private static bool SelectNestedEntities(

      Editor ed,

      out ObjectIdCollection ids,

      out List<FullSubentityPath> paths

    )

    {

      ids = new ObjectIdCollection();

      paths = new List<FullSubentityPath>();

 

      // Loop until cancelled or completed

 

      PromptNestedEntityResult rs;

      PromptNestedEntityOptions pneo =

        new PromptNestedEntityOptions(

          "\nSelect nested entity: "

        );

      pneo.AllowNone = true;

 

      do

      {

        rs = ed.GetNestedEntity(pneo);

 

        if (rs.Status == PromptStatus.OK)

        {

          ids.Add(rs.ObjectId);

 

          FullSubentityPath path = HighlightSubEntity(rs);

          if (path != FullSubentityPath.Null)

            paths.Add(path);

        }

      }

      while (rs.Status == PromptStatus.OK);

 

      // Return whether the function was cancelled

 

      return (rs.Status != PromptStatus.Cancel);

    }

 

    // Unhighlight a set of sub-entities

 

    private static void UnhighlightSubEntities(

      List<FullSubentityPath> paths

    )

    {

      for (int i = 0; i < paths.Count; i++)

      {

        ObjectId[] ids = paths[i].GetObjectIds();

        Entity ent =

          ids[0].GetObject(OpenMode.ForRead) as Entity;

        if (ent != null)

        {

          ent.Unhighlight(paths[i], false);

        }

      }

    }

 

    // Highlight a sub-entity based on its nested

    // selection information.

    // Return the calculated sub-entity path, so

    // the calling application can later unhighlight.

 

    private static FullSubentityPath HighlightSubEntity(

      PromptNestedEntityResult rs

    )

    {

      // Extract relevant information from the prompt object

 

      ObjectId selId = rs.ObjectId;

      List<ObjectId> objIds =

        new List<ObjectId>(rs.GetContainers());

 

      // Reverse the "containers" list

 

      objIds.Reverse();

 

      // Now append the selected entity

 

      objIds.Add(selId);

 

      // Retrieve the sub-entity path for this entity

 

      SubentityId subEnt =

        new SubentityId(SubentityType.Null, System.IntPtr.Zero);

      FullSubentityPath path =

        new FullSubentityPath(objIds.ToArray(), subEnt);

 

      // Open the outermost container, relying on the open

      // transaction...

 

      Entity ent =

        objIds[0].GetObject(OpenMode.ForRead) as Entity;

 

      // ... and highlight the nested entity

 

      if (ent == null)

        return FullSubentityPath.Null;

 

      ent.Highlight(path, false);

 

      // Return the sub-entity path for later unhighlighting

 

      return path;

    }

  }

}

I do expect a few more updates of this code to come… I’d still like to address Xref selection, either implementing it via Long Transactions or calling it out as a special (unsupported) case, as well as some kind of window selection of nested entities, which would be really handy.

blog comments powered by Disqus

Feed/Share

10 Random Posts