A question on how to highlight Polyline in a BlockReference was posted in Autodesk's .NET discussion forum. In my reply, I proposed to use TransientGraphics to render the highlight. Here I put together a quick project to show how easy to achieve that.
By the way, in the past I posted 2 articles on highlighting attributes in BlockReference here and here. However, because attribute (AttributeReference) is owned by BlockReference, while entity we see in BlockReference is actually part of block definition (BlockTableRecord), we cannot highlight it individually. That is where TransientGraphics comes into play: we create a clone of the target entity in block definition, transform its location to where the BlockReference is, and then use the cloned entity as Drawable to draw TransientGraphics.
Here is the class BlockNestedEntityHighlighter:
using System; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; namespace HighlightEntityInBlock { public class BlockNestedEntityHighlighter : IDisposable { private Entity _entClone = null; private readonly TransientManager _tsManager = TransientManager.CurrentTransientManager; private int _colorIndex = 2; public void HighlightEntityInBlock(ObjectId nestedEntId, Matrix3d transform) { ClearHighlight(); using (var tran = nestedEntId.Database.TransactionManager.StartTransaction()) { var ent = (Entity)tran.GetObject(nestedEntId, OpenMode.ForRead); _entClone = ent.Clone() as Entity; tran.Commit(); } _entClone.ColorIndex = _colorIndex; _entClone.TransformBy(transform); _tsManager.AddTransient( _entClone, TransientDrawingMode.Highlight, 128, new IntegerCollection()); } public void Dispose() { ClearHighlight(); } private void ClearHighlight() { if (_entClone != null) { _tsManager.EraseTransient( _entClone, new IntegerCollection()); _entClone.Dispose(); _entClone = null; } } } }
Here is the code running to highlight a selected nested entity from a BlockReference:
using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using CadApp = Autodesk.AutoCAD.ApplicationServices.Application; [assembly: CommandClass(typeof(HighlightEntityInBlock.MyCommands))] namespace HighlightEntityInBlock { public class MyCommands { [CommandMethod("HlEntInBlk")] public static void RunMyCommand() { var dwg = CadApp.DocumentManager.MdiActiveDocument; var ed = dwg.Editor; if (SelectNestedEntityInBlock( ed, out ObjectId nestedEntId, out Matrix3d blkTransform)) { using (var highlighter = new BlockNestedEntityHighlighter()) { highlighter.HighlightEntityInBlock(nestedEntId, blkTransform); ed.GetString("\nPress Enter to continue..."); } ed.PostCommandPrompt(); } else { ed.WriteMessage("\n*Cancel*\n"); } } private static bool SelectNestedEntityInBlock(Editor ed, out ObjectId entId, out Matrix3d blkTransform) { entId = ObjectId.Null; blkTransform = Matrix3d.Identity; var res = ed.GetNestedEntity("\nPick an entity in a block:"); if (res.Status== PromptStatus.OK) { entId = res.ObjectId; blkTransform = res.Transform; ed.WriteMessage($"\nentId: {entId}"); return true; } else { return false; } } } }
See this video clip for the effect:
For simplicity, I hard-coded the highlight color as yellow. If the color of the nested entity is yellow (whether is ByBlock, or ByLayer, or by itself) , the code could be enhanced to choose a different color automatically to make the highlight stand out; f the nested entity is a Polyline, the code could make its global width thicker; the code could also use different line type, or line weight.
Update
Since the author of the original post in Autodesk .NET discussion asked for a VB.NET version of the code shown here, I did a quick conversion and now also post it here:
Class BlockNestedEntityHighlighter:
Imports System Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.GraphicsInterface Namespace HighlightEntityInBlock Public Class BlockNestedEntityHighlighter Implements IDisposable Private _entClone As Entity = Nothing Private ReadOnly _tsManager As TransientManager = TransientManager.CurrentTransientManager Private _colorIndex As Integer = 2 Public Sub HighlightEntityInBlock(ByVal nestedEntId As ObjectId, ByVal transform As Matrix3d) ClearHighlight() Using tran = nestedEntId.Database.TransactionManager.StartTransaction() Dim ent = CType(tran.GetObject(nestedEntId, OpenMode.ForRead), Entity) _entClone = TryCast(ent.Clone(), Entity) tran.Commit() End Using _entClone.ColorIndex = _colorIndex _entClone.TransformBy(transform) _tsManager.AddTransient(_entClone, TransientDrawingMode.Highlight, 128, New IntegerCollection()) End Sub Public Sub Dispose() Implements IDisposable.Dispose ClearHighlight() End Sub Private Sub ClearHighlight() If _entClone IsNot Nothing Then _tsManager.EraseTransient(_entClone, New IntegerCollection()) _entClone.Dispose() _entClone = Nothing End If End Sub End Class End Namespace
CommandClass MyCommands:
Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.Runtime Imports CadApp = Autodesk.AutoCAD.ApplicationServices.Application Imports System.Runtime.InteropServices <Assembly: CommandClass(GetType(HighlightEntityInBlock.MyCommands))> Namespace HighlightEntityInBlock Public Class MyCommands <CommandMethod("HlEntInBlk")> Public Shared Sub RunMyCommand() Dim dwg = CadApp.DocumentManager.MdiActiveDocument Dim ed = dwg.Editor Dim nestedEntId As ObjectId = Nothing, blkTransform As Matrix3d = Nothing If SelectNestedEntityInBlock(ed, nestedEntId, blkTransform) Then Using highlighter = New BlockNestedEntityHighlighter() highlighter.HighlightEntityInBlock(nestedEntId, blkTransform) ed.GetString(vbLf & "Press Enter to continue...") End Using ed.PostCommandPrompt() Else ed.WriteMessage(vbLf & "*Cancel*" & vbLf) End If End Sub Private Shared Function SelectNestedEntityInBlock(ByVal ed As Editor, <Out> ByRef entId As ObjectId, <Out> ByRef blkTransform As Matrix3d) As Boolean entId = ObjectId.Null blkTransform = Matrix3d.Identity Dim res = ed.GetNestedEntity(vbLf & "Pick an entity in a block:") If res.Status = PromptStatus.OK Then entId = res.ObjectId blkTransform = res.Transform ed.WriteMessage($"\nentId: {entId}") Return True Else Return False End If End Function End Class End Namespace
Thank you Norman this post has helped me greatly.
ReplyDeleteI'm having a little trouble calling it, although I don't know much about VBA, how did you call it? You will also provide me the rest of the code.
ReplyDeleteSorry, this is not VBA code. It is AutoCAD .NET API code in an AutoCAD plugin project (an DLL project).
ReplyDelete