In this article, I’ll show how to use Transient Graphics to visually “highlight” certain type of entity (lightweight polyline in my code, but you can easily modify the code to target other type of entity): when user moves the cursor over certain type of entity, the entity is highlighted, while the cursor moves away, the highlighting graphics goes away, too.
Here is the code, followed by some explanation:
using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
[assembly: CommandClass(typeof(TransientGraphicsSample.TGCommands))]
namespace TransientGraphicsSample
{
public class TGCommands
{
public TGCommands()
{
//
// TODO: Add constructor logic here
//
}
#region Polyline Transient Graphics test
private static PolylineTGTracks mTGTracks = null;
private static IPolylineTGFilter mFilter = null;
[CommandMethod("PolyTGOn")]
public static void TurnOnPolylineTG()
{
if (mTGTracks == null) mTGTracks = new PolylineTGTracks();
//Instantiate polyline filter
if (mFilter == null) mFilter = new MyPolylineTGFilter();
Document dwg =
Application.DocumentManager.MdiActiveDocument;
mTGTracks.
SetMouseOverEntityTransientGraphics
(dwg, mFilter, true);
}
[CommandMethod("PolyTGOff")]
public static void TurnOffPolyTG()
{
if (mTGTracks == null) return;
Document dwg =
Application.DocumentManager.MdiActiveDocument;
mTGTracks.SetMouseOverEntityTransientGraphics
(dwg, null, false);
}
#endregion
}
// A class to hold information of transient graphics created
// in a document
public class TGInformation
{
private Document mDwg;
private Editor mEditor;
private Database mDB;
// Targed entity. Should be a Polyine in this example
private ObjectId mCurrentMouseOverObject = ObjectId.Null;
// A polyline object used as transient graphic object
private Polyline mMouseOverTGObject = null;
// Flaging if the transient graphic visual prompt takes effect
private bool mMouseOverEntityMonitored = false;
// Visual prompt's color
private int mTGColorIndex=1;
// Width of the polyline as transient graphic object
private double mLineWidth = 1.0;
// Targeting polyline filter. See more explanation on
// IPolylineTGFilter interface and its implementation
private IPolylineTGFilter mFilter;
// Constructor
public TGInformation
(
Document dwg,
IPolylineTGFilter filter
)
{
mDwg = dwg;
mEditor = mDwg.Editor;
mDB = mDwg.Database;
mFilter = filter;
}
// Overloaded Constructor
public TGInformation
(
Document dwg,
IPolylineTGFilter filter,
double linewidth
)
{
mDwg = dwg;
mEditor = mDwg.Editor;
mDB = mDwg.Database;
mFilter = filter;
mLineWidth = linewidth;
}
public double TGLineWidth
{
set { mLineWidth = value; }
get { return mLineWidth; }
}
// This property indicates if Mouse-Over
// visual effect is set to on or off
public bool MouseOverEntityMonitored
{
set
{
if (mMouseOverEntityMonitored == value)
{
return;
}
if (value)
{
BeginMonitoringMouseOverEntity();
}
else
{
EndMonitoringMouseOverEntity();
}
}
get
{
return mMouseOverEntityMonitored;
}
}
#region private methods
private void BeginMonitoringMouseOverEntity()
{
//Clear previous transient graphic objec
ClearMouseOverTGObject();
mCurrentMouseOverObject = ObjectId.Null;
//Start handling Editor.PointMinotor event
mEditor.PointMonitor +=
new PointMonitorEventHandler(mEditor_PointMonitor);
mMouseOverEntityMonitored = true;
}
private void EndMonitoringMouseOverEntity()
{
//Remove Ediotr.PointMonitor event handler
mEditor.PointMonitor -=
new PointMonitorEventHandler(mEditor_PointMonitor);
mMouseOverEntityMonitored = false;
//Clear exisitn transient graphic object
ClearMouseOverTGObject();
mCurrentMouseOverObject = ObjectId.Null;
}
// Handling Editor.PointMonitor event. so that
//if the cursor is hovering on targeting polyline,
//transient graphic object is created, thus,
//the targeting polyline is highlighted
void mEditor_PointMonitor(object sender,
PointMonitorEventArgs e)
{
FullSubentityPath[] entPaths =
e.Context.GetPickedEntities();
if (entPaths.Length > 0)
{
//When the cursor does hover something
//Get the ObjectId of the entity
FullSubentityPath entPath = entPaths[0];
ObjectId id = entPath.GetObjectIds()[0];
//If the entity is polyline, in this example
if (id.ObjectClass ==
Polyline.GetClass(typeof(Polyline)))
{
//Add your filter here, to decide if
//this polyline is one of the targeting
//polylines in interest
if (mFilter.IsTGTarget(id))
{
//Set the transient graphic highlight
AddOrModifyMouseOverTGObject(id);
}
}
}
else
{
//No entity present below the cursor,
//so clear transient graphic highlight
ClearMouseOverTGObject();
}
}
//Do the transient graphic highlighting
private void AddOrModifyMouseOverTGObject
(ObjectId id)
{
if (mCurrentMouseOverObject == id)
{
//If the cursor is moving over on the
//same targeting polyline, update the
//highlight (changing its color) to
//achieve eye-catching effect
ModifyMouseOverTG();
}
else
{
//Create a new transient graphic object
mCurrentMouseOverObject = id;
AddMouseOverTGObject();
}
}
// Clear existing transient graphic effect
private void ClearMouseOverTGObject()
{
if (mMouseOverTGObject != null)
{
//Remove transient graphic effect
IntegerCollection intCol =
new IntegerCollection();
TransientManager.CurrentTransientManager.
EraseTransient(mMouseOverTGObject, intCol);
//dispose the polyline used to
//show transient graphic effect
mMouseOverTGObject.Dispose();
mMouseOverTGObject = null;
}
}
//Create new transient graphic object
private void AddMouseOverTGObject()
{
//Get the targeting polyline
Polyline target = GetTargetPolyline();
//Create transient graphic polyline,
//which the exactly the same as
//the targeting polyline geometrically
mMouseOverTGObject =
new Polyline(target.NumberOfVertices);
//Set vertices' coordinate and width
for (int i = 0; i < target.NumberOfVertices; i++)
{
Point2d pt = target.GetPoint2dAt(i);
mMouseOverTGObject.AddVertexAt
(i, pt, 0.0, mLineWidth, mLineWidth);
}
//Set color
mMouseOverTGObject.ColorIndex = mTGColorIndex;
//Let the color changes from 1 to 8
//so that when mouse moves, highlighting
//color changes
mTGColorIndex += 1;
if (mTGColorIndex > 8) mTGColorIndex = 1;
mMouseOverTGObject.Closed = target.Closed;
mMouseOverTGObject.SetDatabaseDefaults();
//Add transient graphics
IntegerCollection col = new IntegerCollection();
TransientManager.CurrentTransientManager.
AddTransient
(
mMouseOverTGObject,
TransientDrawingMode.DirectShortTerm,
128,
col
);
}
private void ModifyMouseOverTG()
{
if (mMouseOverTGObject==null)
{
AddMouseOverTGObject();
}
else
{
Polyline target = GetTargetPolyline();
//reset vertices' coordinate, in case the user
//changed the targeting polyline by dragging
//its grip
for (int i = 0;
i < target.NumberOfVertices - 1; i++)
{
Point2d pt = target.GetPoint2dAt(i);
mMouseOverTGObject.SetPointAt(i, pt);
}
//Set color
mMouseOverTGObject.ColorIndex =
mTGColorIndex;
mTGColorIndex += 1;
if (mTGColorIndex > 8) mTGColorIndex = 1;
//Update transient graphics
IntegerCollection col =
new IntegerCollection();
TransientManager.CurrentTransientManager.
UpdateTransient(mMouseOverTGObject, col);
}
}
private Polyline GetTargetPolyline()
{
Polyline target=null;
using (Transaction tran =
mDB.TransactionManager.
StartOpenCloseTransaction())
{
target = tran.GetObject(
mCurrentMouseOverObject,
OpenMode.ForRead) as Polyline;
}
return target;
}
#endregion
}
// A Dictionary collection to hold transient graphics
// information for each opened document
public class PolylineTGTracks :
Dictionary
{
#region public methods
public void SetMouseOverEntityTransientGraphics
(
Document dwg,
IPolylineTGFilter filter,
bool turnOn
)
{
if (turnOn)
{
if (!this.ContainsKey(dwg))
{
TGInformation tgInfo =
new TGInformation(dwg,filter);
this.Add(dwg, tgInfo);
}
//Start monitor mouse moving and
//do the transient graphic highlight
//when targeted polyline is under
//under the mouse cursor
this[dwg].MouseOverEntityMonitored = true;
}
else
{
if (this.ContainsKey(dwg))
{
//Stop transient graphic effect
this[dwg].MouseOverEntityMonitored = false;
}
}
}
//overload, taking a parameter for
//specifc line width
public void SetMouseOverEntityTransientGraphics
(Document dwg, IPolylineTGFilter filter,
double linewidth, bool turnOn)
{
if (turnOn)
{
if (!this.ContainsKey(dwg))
{
TGInformation tgInfo =
new TGInformation(
dwg, filter,linewidth);
this.Add(dwg, tgInfo);
}
else
{
this[dwg].TGLineWidth = linewidth;
}
this[dwg].MouseOverEntityMonitored = true;
}
else
{
if (this.ContainsKey(dwg))
{
this[dwg].MouseOverEntityMonitored
= false;
}
}
}
#endregion
}
// Interface used as filter to find targeting polyline.
// Implement this interface based on your need.
// For example, you can decide if a polyline is the
// targeting entity based on its layer, layout, XData...
public interface IPolylineTGFilter
{
bool IsTGTarget(ObjectId id);
}
//My implementation of IPolylineTGFilter in
//this example: all polylines are targeted
public class MyPolylineTGFilter : IPolylineTGFilter
{
#region PolylineTGFilter Members
public bool IsTGTarget(ObjectId id)
{
//I could do this
//====================
//Entity ent=GetEntityByObjectId(id);
//if ent.Layer!="Layer1") return false;
//=====================
return true;
}
#endregion
}
}
OK, the abundant comments between code lines should be explanative enough, let take look the result of running this code.
To run the code, start AutoCAD (2009/2010), “Netload” the code, draw a couple of polylines, then enter command “PolyTGOn”. Now make cursor move over/out one of the polylines. You would see a very eye-catching highlight appear along the polyline, when the cursor hangs over the target polyline and dispears when the cursor moves out the polyline. See a short video clip here fore the result
You would also notice the highlighting color changes with the move of the cursor, which makes a strong visual prompt, doesn’t it?
Now, open another new drawing and draw a few polylines. However, if you move the mouse cursor over on those polylines, no visual highlighting shows, until you issue command “PolyTGOn” to this drawing.
Of course, if you now switch back to previous drawing, the transient graphic effect is still there until you issue command “PolyTGOff”.
An obviously possible enhancement to this code would be to allow user to configure MyPolylineTGFilter class, so that the transient graphic effect can appear on desired entities according to user inputs.