This is a nice sample provided by Stephen Preston, who manages DevTech’s Americas team. Stephen has put this together in anticipation of his upcoming AU class on the overrule API introduced in AutoCAD 2010. [I know the final class list has not yet been announced, but Stephen is co-owner of the Customization & Programming track at this year’s AU and presumably has the inside skinny on the selected classes. Which means he has a head-start on preparing his material, lucky fellow. :-)]
The sample allows the user to enter a text string that it uses to highlight any block containing that string in its name. This is quite handy for identifying the instances of a particular block in a drawing, but it might also be modified to highlight other objects (you might want to highlight mis-spelt words or standards violations, for instance).
Here’s the C# code, reformatted for this blog:
using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.GraphicsInterface;
namespace MyCustomFilterOverrule
{
// This is our custom DrawableOverrule class. We're just
// overruling WorldDraw and IsApplicable.
// This class is implemented as a singleton class, and
// includes subroutines that are called by the CommandMethods
// in another class
public class MyDrawOverrule : DrawableOverrule
{
// Where properties have been defined, use the property rather
// than the raw variable.
// I'm using properties where I need some additional logic to
// run as I get/set the variable.
// The text we'll search for in our block name.
private string mTxt;
// Color Index of block highlight
private short mColor = 3;
// Used to track whether this Overrule has been registered
// (so we don't try to register it more than once).
private bool mRegistered = false;
// Used to store one and only instance of our singleton class
private static MyDrawOverrule mSingleton;
// Used to reset Overruling value to the value it had before
// we switched them on. (There may be other overrules in place)
private static bool mOldOverruleValue;
// The color we highlight blocks with
private short HighlightColor
{
get { return mColor; }
set { if (value >= 0 && value <= 127) mColor = value; }
}
// The text we'll search for in the block name
private string SearchText
{
get { return mTxt; }
set { mTxt = value; }
}
// Private constructor because its a singleton
private MyDrawOverrule()
{
// Do nothing
}
// Shared propery to return our singleton instance
// (and instantiate new instance on first call)
public static MyDrawOverrule GetInstance
{
get
{
if (mSingleton == null)
{
mSingleton = new MyDrawOverrule();
}
return mSingleton;
}
}
private void InitOverrule()
{
if (!mRegistered)
{
Overrule.AddOverrule(
RXObject.GetClass(typeof(BlockReference)), this, false
);
SetCustomFilter();
mOldOverruleValue = Overrule.Overruling;
mRegistered = true;
}
Overrule.Overruling = true;
}
// Prompts user to select the color index they want to
// highlight blocks with
public void SetColor()
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
PromptIntegerOptions opts =
new PromptIntegerOptions(
"\nEnter block finder color index: "
);
opts.DefaultValue = HighlightColor;
opts.LowerLimit = 0;
opts.UpperLimit = 127;
opts.UseDefaultValue = true;
PromptIntegerResult res = ed.GetInteger(opts);
// If requested highlight color is a new color,
// then we want to change it
if (res.Status == PromptStatus.OK &&
HighlightColor != res.Value)
{
HighlightColor = (short)res.Value;
// Regen is required to update changes on screen
ed.Regen();
}
}
public void FindText()
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage(
"\nCurrent block search text is \"{0}\".", SearchText
);
PromptStringOptions opts =
new PromptStringOptions(
"\nEnter new block search text: "
);
PromptResult res = ed.GetString(opts);
// If the user cancelled then we exit the command
if (res.Status != PromptStatus.OK)
return;
// If the user didn't type any text then we remove
// the overrule and exit
if (res.StringResult == "")
{
SearchText = "";
ResetBlocks();
}
else
{
// Set search text for Overrule to that entered by user
SearchText = res.StringResult.ToUpper();
InitOverrule();
// Turn Overruling on
Overrule.Overruling = true;
// Regen is required to update changes on screen.
ed.Regen();
}
}
// Removes our overrules
public void ResetBlocks()
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
Overrule.Overruling = mOldOverruleValue;
if (mRegistered)
{
Overrule.RemoveOverrule(
RXObject.GetClass(typeof(BlockReference)), this
);
mRegistered = false;
ed.Regen();
}
}
// Overrule WorldDraw so we can draw our additional
// graphics
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
// Better safe than sorry - check it really is a
// BlockReference before continuing.
BlockReference br = drawable as BlockReference;
if (br != null)
{
// Now we want to draw a green box around the attributes
// extents
Extents3d ext = (Extents3d)br.Bounds;
Point3d maxPt = ext.MaxPoint;
Point3d minPt = ext.MinPoint;
Point3dCollection pts = new Point3dCollection();
// These are the vertices of the highlight box
pts.Add(new Point3d(minPt.X, minPt.Y, minPt.Z));
pts.Add(new Point3d(minPt.X, maxPt.Y, minPt.Z));
pts.Add(new Point3d(maxPt.X, maxPt.Y, minPt.Z));
pts.Add(new Point3d(maxPt.X, minPt.Y, minPt.Z));
// Store current filltype and set to FillAlways
FillType oldFillType = wd.SubEntityTraits.FillType;
wd.SubEntityTraits.FillType = FillType.FillAlways;
// Store old graphics color and set to the color we want
short oldColor = wd.SubEntityTraits.Color;
wd.SubEntityTraits.Color = HighlightColor;
// Draw the filled polygon
wd.Geometry.Polygon(pts);
// Restore old settings
wd.SubEntityTraits.FillType = oldFillType;
wd.SubEntityTraits.Color = oldColor;
}
// Let the overruled Drawable draw itself.
return base.WorldDraw(drawable, wd);
}
// This function is called if we call SetCustomFilter on our
// custom overrule.
// We add our own code to return true if the BlockReference
// passed in is one we want to highlight.
public override bool IsApplicable(RXObject overruledSubject)
{
// If it's a BlockReference, we check if the Block Name
// contains our string
BlockReference br = overruledSubject as BlockReference;
if (br != null && SearchText != "")
{
// Returns whether the filter is applicable to this object
return br.Name.Contains(SearchText);
}
// Only get to here if object isn't a BlockReference
return false;
}
}
// Our command class, which relays commands to MyDrawOverrule.
public class myPlugin
{
[CommandMethod("SHOWBLOCKS")]
public static void FindText()
{
MyDrawOverrule.GetInstance.FindText();
}
[CommandMethod("SHOWCOLOR")]
public static void SetColor()
{
MyDrawOverrule.GetInstance.SetColor();
}
}
}
Here’s what happens when we OPEN the “Mechanical – Multileaders.dwg” sample drawing, NETLOAD our application and use SHOWBLOCKS to look for the “M045” text string:
Here’s what we see if we broaden the search to include all blocks with the string “M0” in their name and change the highlight colour to 1 using SHOWCOLOR:
To clear the selection, the user simply has to run SHOWBLOCKS and specify an empty string as the search term.
Stephen will be presenting both C# and VB.NET versions of this sample application during his class at this year’s AU. If you find overrules interesting, then I strongly recommend signing up for the session (I’ll let you know when registrations are open). I’m sure that during the class Stephen will be demonstrating other interesting capabilities made available to AutoCAD .NET developers by this very cool API.