@_gile shows an example of how to call Command() from a click handler on a PaletteSet, but if you have many buttons on a paletteSet or other modeless UI that need to do this, then you should try to avoid the "copy and paste" style of programming that replicates all of that code in every button click event handler you need to use it from, and instead consolidate the code into a reusable method that makes things a bit simpler at the click-handler level, and also safer.
Another thing, is that I never recommend the use of ExecuteInCommandContextAsync() without issuing a stern caveat emptor, which is that this is a highly-volatile API that can crash AutoCAD very easily (any exception that's thrown by the delegate will do that).
So with that, below is a class with an extension method you can add to your project, that makes calling Command() from the click-handler of a button both easier and safer, that I've relied on for some time:
public static partial class DocumentCollectionExtensions
{
/// <summary>
/// A version of the Editor's Command() method that
/// can be safely called from the application context.
///
/// This method targets the DocumentCollection and
/// always operates on the active document.
///
/// Calls to the method can be awaited to execute
/// code that follows after the command sequence
/// has executed.
///
/// Important:
///
/// Calls to this method should _always_ be wrapped
/// in a try{} block, that's followed by a catch{}
/// block that handles any exception that may be
/// thrown by the Editor's Command() method (e.g.,
/// ErrorStatus.InvalidInput).
///
/// Failing to catch exceptions thrown by this method,
/// or by any statements that follow an await'ed call
/// to this method will most-likely crash AutoCAD.
///
/// </summary>
/// <param name="args">The command arguments.</param>
public static async Task CommandAsync(this DocumentCollection docs,
params object[] args)
{
if(docs == null)
throw new ArgumentNullException(nameof(docs));
if(!docs.IsApplicationContext)
throw new InvalidOperationException("invalid context");
Document doc = docs.MdiActiveDocument;
Task task = Task.CompletedTask;
await docs.ExecuteInCommandContextAsync(
delegate (object unused)
{
try
{
doc.Editor.Command(args);
return task;
}
catch(System.Exception ex)
{
return task = Task.FromException(ex);
}
},
null
);
if(task.IsFaulted)
{
throw task.Exception ??
new AggregateException(
new InvalidOperationException("Unspecified error"));
}
}
}
With the above class added to your project, executing commands from modeless button click handlers is no more complicated than this:
private async void MyButton_Click(object sender, EventArgs e)
{
DocumentCollection docs = Application.DocumentManager;
await docs.CommandAsync("_-PDFATTACH", "path", "pagetext", Point3d.Origin, 1.0, 0.0);
}
What is shown above is the absolute bare-minimum amount of code needed to execute commands, but is not the recommended way of using it, because of the danger of an exception, which will crash AutoCAD if left unhandled.
This is a safer and more-correct implementation:
private async void MyButton_Click(object sender, EventArgs e)
{
DocumentCollection docs = Application.DocumentManager;
try // <- this is NOT optional!
{
await docs.CommandAsync("_-PDFATTACH", "path", "pagetext", Point3d.Origin, 1.0, 0.0);
// TODO: add code here that runs *after* the commands have executed
}
catch(System.Exception ex)
{
docs.MdiActiveDocument.Editor.WriteMessage($"Operation failed: {ex.Message}");
}
}