Hi,
Since AutoCAD 2015, two instance methods have been added to the Editor class: Editor.Command() and Editor.CommandAsync().
These method replace the need to
P/Invoke acedCmd() or call the
non-public Editor.RunCommand() to synchronously run AutoCAD commands.
Note that these methods are no longer available.
The docs about these methods are more than laconic: "
This is Command, a member of class Editor."
Googling for CommandAsync I only found 3 examples: a thread on
Kean Walmsley's blog, another one from
AutoCAD DevBlog and a code sample on
GitHub.
So, I made some tests to try to deeply understand how work these new methods and share the results here.
About the methods arguments typeBoth Editor.Command() and Editor.CommandAsync() accept a params array of object as argument. This means the arguments (the command name and the command inputs) can be passed separated by a comma (no need to build an array as in the examples on the Adn DevBlog and GitHub).
The command inputs can be any strong .NET type (int, double, Point2d, Point3d, ObjectId, SelectionSet, ...).
As with the LISP function
command, numbers or points can be passed as string (as on command line).
An empty string ("") stands for Enter and backslash (@"\") for a pause for user input. For the last one, a constant have added to the Editor class : Editor.PauseToken.
Command() vs CommandAsync()That said, the main question is: what is the difference between Command() and CommandAsync(), in other words, when must we have to use CommandAsync() rather than Command().
This seems to be the purpose of Kean's example, but I do not think it is very relevent: the Editor.Command() also accepts pauses for user input.
Going a little further in the comparison with de command LISP function, I tried to mimic some LISP behaviors:
- call multiple commands in a single expression:
- split the the command arguments in multiple expressions:
In both cases, it requires to use CommandAsync(). With Command() method, we have to call it for each command (the following ZoomEntLast() method is also used in later examples)
public void ZoomEntLast()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ObjectId entlast = Autodesk.AutoCAD.Internal.Utils.EntLast();
if (entlast != ObjectId.Null)
{
ed.Command("_.ZOOM", "_object", entlast, "");
ed.Command("_.ZOOM", ".8x");
}
}
[CommandMethod("CMD1")]
public async void Cmd1()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
await ed.CommandAsync(
"_.LINE",
new Point3d
(-10.0,
-10.0,
0.0),
new Point3d
(10.0,
10.0,
0.0),
"",
"_.CIRCLE", Point3d.Origin, 10.0);
ZoomEntLast();
}
[CommandMethod("CMD2")]
public async void Cmd2()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
await ed.CommandAsync("_.CIRCLE");
await ed.CommandAsync("0,0");
await ed.CommandAsync("10");
ZoomEntLast();
}
The last shown behavior is interesting, for example, to pass a list of arguments to a command (e.g. a points list for LINE, PLINE, SPLINE).
This also requires the CommandAsync using.
[CommandMethod("CMD3")]
public async void Cmd3()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Point2d[] pts =
{
};
await ed.CommandAsync("_.PLINE");
foreach (Point2d pt in pts)
{
await ed.CommandAsync(pt);
}
await ed.CommandAsync("_close");
ZoomEntLast();
}
SynchronicityAnother "advanced using of command" in LISP is to launch a command and let the user complete it before continuing the program.
With the Editor.Command() method, all the 'while stuff' is implicit, and the program will wait for the command completed:
[CommandMethod("CMD4")]
public void Cmd4()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.Command("_.PLINE");
ZoomEntLast();
}
To be synchronous, the Editor.CommandAsync() needs to explicitly complete the command (iow do the while stuff as in LISP). This may be interesting if some other task may interact during the user inputs, for example, collecting the newly created lines:
[CommandMethod("CMD5")]
public async void Cmd5()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
HashSet
<ObjectId
> ids
= new HashSet
<ObjectId
>();
await ed.CommandAsync("_.LINE", Editor.PauseToken);
while (((string)Application.GetSystemVariable("CMDNAMES")).Contains("LINE"))
{
try
{
await ed.CommandAsync(Editor.PauseToken);
ids.Add(Autodesk.AutoCAD.Internal.Utils.EntLast());
}
catch { break; } // eUserBreak (Cancel) handling
}
Database db = HostApplicationServices.WorkingDatabase;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
foreach (ObjectId id in ids)
{
Entity ent = (Entity)tr.GetObject(id, OpenMode.ForWrite);
ent.ColorIndex = 1;
}
tr.Commit();
}
}