November 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            










« Coming soon to Autodesk Labs… “Plugin of the Month” | Main | API wishlist surveys: a reminder and some encouragement »

July 27, 2009

Providing information on AutoCAD objects in a tooltip using .NET

One of the responses to my last post on the “Plugin of the Month” asked about showing information on an AutoCAD drawing object via a tooltip. Other than using the standard rollover tooltip properties mechanism, as shown in this previous post, the best way to achieve this is via a PointMonitor.

In the below C# code we check which object ore objects are being hovered over, get information about those objects and add them to the tooltip.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

 

public class PointMonitorTooltips

{

  [CommandMethod("SM")]

  public static void StartMonitor()

  {

    Editor ed =

      Application.DocumentManager.MdiActiveDocument.Editor;

    ed.PointMonitor +=

      new PointMonitorEventHandler(ed_PointMonitor);

  }

 

  [CommandMethod("XM")]

  public static void StopMonitor()

  {

    Editor ed =

      Application.DocumentManager.MdiActiveDocument.Editor;

    ed.PointMonitor -=

      new PointMonitorEventHandler(ed_PointMonitor);

  }

 

  static void ed_PointMonitor(object sender, PointMonitorEventArgs e)

  {

    Editor ed = (Editor)sender;

    Document doc = ed.Document;

 

    if (!e.Context.PointComputed)

      return;

 

    try

    {

      // Determine the size of the selection aperture

 

      short pickbox =

        (short)Application.GetSystemVariable("PICKBOX");

      Point2d extents =

        e.Context.DrawContext.Viewport.GetNumPixelsInUnitSquare(

          e.Context.ComputedPoint

        );

      double boxWidth = pickbox / extents.X;

      Vector3d vec =

        new Vector3d(boxWidth / 2, boxWidth / 2, 0.0);

 

      // Do a crossing selection using a centred box the

      // size of the aperture

 

      PromptSelectionResult pse =

        ed.SelectCrossingWindow(

          e.Context.ComputedPoint - vec,

          e.Context.ComputedPoint + vec

        );

 

      if (pse.Status != PromptStatus.OK ||

          pse.Value.Count <= 0)

        return;

 

      // Go through the results of the selection

      // and detect the curves

 

      string curveInfo = "";

 

      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        // Open each object, one by one

 

        ObjectId[] ids = pse.Value.GetObjectIds();

        foreach (ObjectId id in ids)

        {

          DBObject obj =

            tr.GetObject(id, OpenMode.ForRead);

          if (obj != null)

          {

            // If it's a curve, get its length

 

            Curve cv = obj as Curve;

            if (cv != null)

            {

              double length =

                cv.GetDistanceAtParameter(cv.EndParam);

 

              // Then add a message including the object

              // type and its length

 

              curveInfo +=

                obj.GetType().Name + "'s length: " +

                string.Format("{0:F}", length) + "\n";

            }

          }

        }

        // Cheaper than aborting

 

        tr.Commit();

      }

 

      // Add the tooltip of the lengths of the curves detected

 

      if (curveInfo != "")

        e.AppendToolTipText(curveInfo);

    }

    catch

    {

      // Not sure what we might get here, but not real action

      // needed (worth adding an Exception parameter and a

      // breakpoint, in case things need investigating).

    }

  }

}

During the command we perform a selection, using Editor.SelectCrossingWindow() specifying a window based on the selected point, using a window that’s about the size of the selection aperture (at least that’s what I’m trying to do: let me know if you’ve got a better solution for this). Having some amount of “fuzz” in the selection allows us to not have to hover very precisely over the object, which may or may not be what you’re looking for.

We then iterate through the objects, gathering information about the objects we care about. In this case we’re looking for curves: when we find one, we add its length (along with its object type) to the string we eventually add to the tooltip via AppendToolTipText().

There are two commands: SM adds the monitor, and XM removes it. Bear in mind that the event fires for all sorts of input events - including keystrokes – so you can actually cause problems unless you’re careful. As an example, I wasn’t previously checking the PointComputed status of the PointManager’s context, which is false if we’re looking at keyboard input. The code would go ahead and try to select geometry, which – for some reason unknown to me – cause the character in the keystroke buffer to be lost. Which meant that entering any command would only result in the last command being executed (via the return being processed – that one got through, OK :-). All this to point out that care should be taken when relying on events that are so integrated so deeply into AutoCAD’s user input mechanism.

PointMonitor tooltip over a single line So let’s see what we get after NETLOADing the module, running the SM command and hovering over a curve, in this case a line. We see a tooltip appear containing the line’s length.

And if we do the same with a lot more intersecting objects, we see that we also get the PointMonitor tooltip over lots of different curvesopportunity to provide information on those. It’s ultimately our choice how we manage the selection process and what we do with the results (although please don’t assume you can attempt everything from this kind of very frequently fired event: there are a number of areas where you may find things failing, especially if there’s any interactive component).

 

Update:

Thanks to Tony Tanzillo for keeping me honest (see his comments, below). The below code addresses a few issues with the version shown above, and should be used instead.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

 

public class PointMonitorTooltips

{

  [CommandMethod("SM")]

  public static void StartMonitor()

  {

    Editor ed =

      Application.DocumentManager.MdiActiveDocument.Editor;

    ed.PointMonitor +=

      new PointMonitorEventHandler(ed_PointMonitor);

  }

 

  [CommandMethod("XM")]

  public static void StopMonitor()

  {

    Editor ed =

      Application.DocumentManager.MdiActiveDocument.Editor;

    ed.TurnForcedPickOn();

    ed.PointMonitor -=

      new PointMonitorEventHandler(ed_PointMonitor);

  }

 

  static void ed_PointMonitor(object sender, PointMonitorEventArgs e)

  {

    Editor ed = (Editor)sender;

    Document doc = ed.Document;

 

    try

    {

      FullSubentityPath[] paths =

        e.Context.GetPickedEntities();

 

      // Go through the results of the selection

      // and detect the curves

 

      string curveInfo = "";

 

      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        // Open each object, one by one

 

        foreach (FullSubentityPath path in paths)

        {

          ObjectId[] ids = path.GetObjectIds();

          if (ids.Length > 0)

          {

            ObjectId id = ids[ids.GetUpperBound(0)];

 

            DBObject obj =

              tr.GetObject(id, OpenMode.ForRead);

            if (obj != null)

            {

              // If it's a curve, get its length

 

              Curve cv = obj as Curve;

              if (cv != null)

              {

                double length =

                  cv.GetDistanceAtParameter(cv.EndParam) -

                  cv.GetDistanceAtParameter(cv.StartParam);

 

                // Then add a message including the object

                // type and its length

 

                curveInfo +=

                  obj.GetType().Name + "'s length: " +

                  string.Format("{0:F}", length) + "\n";

              }

            }

          }

        }

        // Cheaper than aborting

 

        tr.Commit();

      }

 

      // Add the tooltip of the lengths of the curves detected

 

      if (curveInfo != "")

        e.AppendToolTipText(curveInfo);

    }

    catch

    {

      // Not sure what we might get here, but not real action

      // needed (worth adding an Exception parameter and a

      // breakpoint, in case things need investigating).

    }

  }

}

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e2011571477529970c

Listed below are links to weblogs that reference Providing information on AutoCAD objects in a tooltip using .NET:

blog comments powered by Disqus

Feed/Share

10 Random Posts