Monday, March 15, 2021

Highlight Entity In Block - Upated: Added VB.NET Code

 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;
 
[assemblyCommandClass(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 ObjectIdByVal 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
 
<AssemblyCommandClass(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, <OutByRef entId As ObjectId, <OutByRef blkTransform As Matrix3dAs 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

3 comments:

Anonymous said...

Thank you Norman this post has helped me greatly.

Angelina Brown said...

I'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.

Norman Yuan said...

Sorry, this is not VBA code. It is AutoCAD .NET API code in an AutoCAD plugin project (an DLL project).

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.