View Categories

Using Editor.CommandAsync to chain multiple commands involving user input

2 min read

Motivation #

You might want to chain two or more AutoCAD/Civil 3D commands that involve user interaction — for example, generating tables where the user needs to click “OK” or “Cancel” in a dialog box.

Editor.Command #

According to this post on TheSwamp, the Editor.Command() method should do the job. In theory, it should wait for user input before proceeding to the next command.

[CommandMethod(nameof(ConsecutiveCommand))]
public void ConsecutiveCommand()
{
    Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument
        .Editor.Command("AeccAddNetworkPipeTable");
    Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument
        .Editor.Command("AeccAddNetworkStructTable");
}

In other words, the above ConsecutiveCommand should first present the user with the dialog box for adding a Network Pipe Table (_AeccAddNetworkPipeTable).

The user can configure the settings and click OK, and only after that should the next dialog box (_AeccAddNetworkStructTable) appear.

What actually happens #

In practice, the first command (_AeccAddNetworkPipeTable) runs correctly, but the second command (_AeccAddNetworkStructTable) is ignored — as if it never existed.

The reason for this behavior isn’t immediately obvious.

Solution: Editor.CommandAsync #

The solution is to use Editor.CommandAsync, as explained in this Autodesk forum post.

You’ll need to call CommandAsync within a loop to ensure that each command completes before the next one runs.

Here’s a wrapper class that encapsulates the mechanism:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.ApplicationServices.Core;
using Autodesk.AutoCAD.EditorInput;
using System;
using System.Threading.Tasks;
using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application;

public class CommandSequenceRunner
{
    private readonly Editor _editor;

    public CommandSequenceRunner(Editor editor)
    {
        _editor = editor ?? throw new ArgumentNullException(nameof(editor));
    }

    /// <summary>
    /// Runs AutoCAD commands sequentially, waiting only for
    /// interactive commands that remain active in CMDNAMES.
    /// </summary>
    public async Task RunAsync(params string[] commands)
    {
        for (int i = 0; i < commands.Length; i++)
        {
            string cmd = commands[i];
            if (string.IsNullOrWhiteSpace(cmd))
                continue;

            await _editor.CommandAsync(cmd);

            // Wait only if the command remains active (interactive)
            string baseName = GetCommandBaseName(cmd);
            if (IsCommandStillRunning(baseName))
            {
                try
                {
                    while (IsCommandStillRunning(baseName))
                        await _editor.CommandAsync(Editor.PauseToken);
                }
                catch
                {
                    break;
                }
            }
        }
    }

    private static bool IsCommandStillRunning(string name)
        => GetActiveCmdNames().Contains(name, StringComparison.OrdinalIgnoreCase);

    private static string GetActiveCmdNames()
        => (string)Application.GetSystemVariable("CMDNAMES");

    private static string GetCommandBaseName(string cmd)
    {
        var first = cmd.Split(' ', '\t')[0];
        return first.TrimStart('.', '_');
    }
}

And here’s how to use it:

var ed = Application.DocumentManager.MdiActiveDocument.Editor;
var runner = new CommandSequenceRunner(ed);

await runner.RunAsync(
    "AeccAddNetworkPipeTable",
    "AeccAddNetworkStructTable");

Powered by BetterDocs