Kean Walmsley


  • About the Author
    Kean on Google+

July 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    








« DevTV on AutoCAD 2011’s Associative Surfaces API | Main | Changing the layer or color of a nested entity using .NET (take 2) »

August 06, 2010

Changing the layer or color of a nested entity using .NET

In a recent comment, Adam requested the code from this previous post be modified to work in the same way as another, more recent, post:

Is it possible to get a version of this code where the user is prompted to create a selection set of nested objects first, and then being prompted for the new colour of the objects?

i.e is it possible to get the code for changing colour working the same way as it does for changing layer, as detailed here?

This would allow the user to pick and choose the objects affected, rather than making a blanket change to all nested objects.

The below code contains a new command – CNC, for Change Nested Color – which complements the existing CNL (Change Nested Layer) command. I took this opportunity to streamline the code based on previous feedback I’d received from Tony Tanzillo and Glenn Ryan (I was manually reversing a list rather than letting the CLR do it for me).

I’ve also adjusted the previous approach to unhighlight entities more carefully, rather than just REGENing the highlighting away, as I’ve found this can cause some minor issues with roll-over highlighting.

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;

 

      // 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)

                {

                  ent.Layer = newLay;

                }

              }

            }

            else

            {

              ed.WriteMessage(

                "\nLayer not found in current drawing."

              );

            }

            UnhighlightSubEntities(paths);

          }

        }

        tr.Commit();

 

        // Regen reflects the new layer

 

        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;

 

      // 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)

              {

                ent.ColorIndex = pir.Value;

              }

            }

          }

          UnhighlightSubEntities(paths);

        }

        tr.Commit();

      }

 

      // Regen reflects the new color

 

      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;

      do

      {

        rs = ed.GetNestedEntity("\nSelect nested entity: ");

 

        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);

 

      // Cancel is the status when "enter" is used to

      // terminate the selection, which means we can't

      // use it to distinguish from an actual

      // cancellation.

 

      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;

    }

  }

}

As we select the individual sub-entities they are highlighted appropriately:

Selecting the rim of our car blockAnd we can then choose out colour to which the various sub-entities will be changed:

Our car with a yellow wheel hubI’m still moderately unhappy about the fact that cancelling the selection loop doesn’t drop out of the command directly (the user has to cancel the subsequent prompt as well, which is a little cumbersome). This is because the sub-entity selection method returns a “cancel” status when the user selects “enter” – which is how the code knows the selection loop should be exited. So when a genuine cancellation occurs, our code can’t tell. I expect that we could jump through some hoops (such as detecting key-presses, etc.) to avoid this, but I’m going to choose to continue to live with it, for now.

blog comments powered by Disqus

10 Random Posts