Since posting three different options for Zooming to a Window or Entity inside AutoCAD, I've had a few discussions with a developer on how best to implement this cleanly. The requirement is to change the AutoCAD view via a smooth view transition (currently not exposed via any kind of view-modification API, only via the ZOOM command), but also to hide the fact we're sending commands to the command-line to do so.
While we were discussing, I remembered an old friend, the NOMUTT system variable, which allows almost all command-line noise to be filtered out - even the "Command:" prompt disappears. While this technique is useful for this specific situation, it's also potentially very useful for many other instances where sending commands are currently the best available method of accessing particular functionality inside AutoCAD.
A word of caution: setting NOMUTT to 1 can lead to severe user-disorientation. Please remember to set the variable back to 0, afterwards!
Here's the modified C# code showing this technique - this time using SendStringToExecute() from the .NET API, rather than the COM API's synchronous SendCommand():
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
namespace ZoomSmoothlyAndQuietly
{
public class Commands
{
// Zoom to a window specified by the user
[CommandMethod("LZW")]
static public void LoudZoomWindow()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Point2d min, max;
if (GetWindowForZoom(ed, out min, out max))
ZoomWin(ed, min, max, false);
}
[CommandMethod("QZW")]
static public void QuietZoomWindow()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Point2d min, max;
if (GetWindowForZoom(ed, out min, out max))
ZoomWin(ed, min, max, true);
}
// Get the coordinates for a zoom
static private bool GetWindowForZoom(
Editor ed, out Point2d min, out Point2d max
)
{
min = new Point2d();
max = new Point2d();
PromptPointOptions ppo =
new PromptPointOptions(
"\nSpecify first corner:"
);
PromptPointResult ppr =
ed.GetPoint(ppo);
if (ppr.Status != PromptStatus.OK)
return false;
min =
new Point2d(ppr.Value.X, ppr.Value.Y);
PromptCornerOptions pco =
new PromptCornerOptions(
"\nSpecify opposite corner: ",
ppr.Value
);
ppr = ed.GetCorner(pco);
if (ppr.Status != PromptStatus.OK)
return false;
max =
new Point2d(ppr.Value.X, ppr.Value.Y) ;
return true;
}
// Zoom by sending a command
private static void ZoomWin(
Editor ed, Point2d min, Point2d max, bool quietly
)
{
string lower =
min.ToString().Substring(
1,
min.ToString().Length - 2
);
string upper =
max.ToString().Substring(
1,
max.ToString().Length - 2
);
string cmd =
"_.ZOOM _W " + lower + " " + upper + " ";
if (quietly)
{
// Get the old value of NOMUTT
object nomutt =
Application.GetSystemVariable("NOMUTT");
// Add the string to reset NOMUTT afterwards
cmd += "_NOMUTT " + nomutt.ToString() + " ";
// Set NOMUTT to 1, reducing cmd-line noise
Application.SetSystemVariable("NOMUTT", 1);
}
// Send the command(s)
ed.Document.SendStringToExecute(
cmd, true, false, !quietly
);
}
}
}
Here's what happens when we run the "loud" command (LZW), and then the "quiet" one (QZW):
Command: LZW
Specify first corner:
Specify opposite corner:
Command: _.ZOOM
Specify corner of window, enter a scale factor (nX or nXP), or
[All/Center/Dynamic/Extents/Previous/Scale/Window/Object] <real time>: _W
Specify first corner: 13.1961936276982,12.972925917324 Specify opposite corner:
30.6221132095147,0.482638801189623
Command: QZW
Specify first corner:
Specify opposite corner:
Command:
Both versions of the command deliver the results in terms of smooth view transitions, but the quiet version reduces the command-line clutter by a) passing a flag to SendStringToExecute() to stop the command from being echoed and b) setting the NOMUTT system variable to prevent the command "muttering" from being echoed. It uses a simple trick to reset the system variable afterwards by appending "_NOMUTT 0 " to the string to be executed (which we don't see, as NOMUTT is still set to 1 :-). Assuming the ZOOM command terminates correctly, the NOMUTT system variable should be reset: there is a slight risk that something causes ZOOM to fail, at which point it might be worth checking NOMUTT from time to time elsewhere in your app: as mentioned earlier, having NOMUTT set to 1 can be very disconcerting for the user. I should, in fact, probably just force NOMUTT to 0 after the ZOOM (if you check the the above code, you'll see we reset it to the prior value, which is generally a good technique when modifying system variables as they retain the value previously chosen by the user). Anyway - I'll leave the final choice up to you, but do be aware of the risk.
Update:
Undo is a problem with this implementation - havin NOMUTT as a separate command leaves it at risk of being undone by the user (leaving the system in a scary, silent state). This post presents an enhanced approach which provides much better support for undo.