API queue command "CommandId" cannot be invoked in this context

API queue command "CommandId" cannot be invoked in this context

ModPlus
Enthusiast Enthusiast
3,021 Views
16 Replies
Message 1 of 17

API queue command "CommandId" cannot be invoked in this context

ModPlus
Enthusiast
Enthusiast

I am trying to post a Revit command from a dockable panel. Some commands post without problems and some do not post at all. For example, the "Select by ID" command. The following message appears in the log:

 

Jrn.Directive _
"IdleTimeTaskInvocation" , "2.PostCommandTask" , 0 _
, "{}"
' 0:< API_SUCCESS { API queue command ID_SELECT_BY_ID cannot be invoked in this context. }
'H 21-Aug-2024 10:25:19.159; 0:<
Jrn.Directive _
"IdleTimeTaskInvocation" , "2.PostCommandTask" , 0 _
, "end"

 

When I initialize my plugin in the OnStartup method, I get an instance of UIApplication:

 

public static UIApplication UiApplication { get; private set; }

public void OnStartup(UIControlledApplication application)
{
    _uiControlledApplication.Idling += UiControlledApplicationOnIdling;
}

private void UiControlledApplicationOnIdling(object sender, IdlingEventArgs e)
{
    if (sender is UIApplication application && HasTabAndPanel())
    {
        UiApplication = application;

 

 

I post the command like this:

 

App.UiApplication.PostCommand(commandId);

 

 

I tried posting the command through Dispatcher, which I also received in OnStartup:

 

App.Dispatcher.Invoke(() => App.UiApplication.PostCommand(commandId));

 

 

And through IExternalEventHandler, which I also created in the OnStartup method:

 

App.RevitEvent.Run(() => App.UiApplication.PostCommand(commandId));

 

If I post the command from a modless window, there is no such problem. Even IExternalEventHandler doesn't need to be used. So how to correctly post a command from a dockable panel?

 

0 Likes
Accepted solutions (1)
3,022 Views
16 Replies
Replies (16)
Message 2 of 17

jeremy_tammik
Alumni
Alumni

You dockable panel lives in a modeless environment outside of a valid Revit API context. 

  

Therefore, you cannot make any valid Revit API calls from it directly at all. Not PostCommand nor anything else.

  

To achieve what you are asking for, you can implement a external event. In your modeless dockable panel code, you can raise the external event. Then, at some point in the future, Revit will call the external event handler, its Execute method. The Execute method provides a valid Revit API context from which PostCommand and other Revit API methods can be called.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 17

jeremy_tammik
Alumni
Alumni

You say, if I post the command from a modeless window, there is no such problem.

   

That is unfortunate; there should be a problem!

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 4 of 17

ModPlus
Enthusiast
Enthusiast

@jeremy_tammik wrote:

You dockable panel lives in a modeless environment outside of a valid Revit API context. 

  

Therefore, you cannot make any valid Revit API calls from it directly at all. Not PostCommand nor anything else.

  

To achieve what you are asking for, you can implement a external event. In your modeless dockable panel code, you can raise the external event. Then, at some point in the future, Revit will call the external event handler, its Execute method. The Execute method provides a valid Revit API context from which PostCommand and other Revit API methods can be called.

  


As I wrote in the post - I tried an external event. This does not solve the problem with posting commands

0 Likes
Message 5 of 17

jeremy_tammik
Alumni
Alumni

You say, as I wrote in the post - I tried an external event. This does not solve the problem with posting commands.

  

Sorry, I still do not see exactly how you are setting up the interaction with the external event, nor how it is being raised from the modeless context. I do see that you registered an Idling event handler. That is something else, not an external event. I do no know what your variables App refer to, or RevitEvent. I am getting confused about what you are doing, more and more.

  

The solution is very simple really, and quite clearly and cleanly demonstrated by the Revit SDK samples ModelessForm_ExternalEvent and ModelessForm_IdlingEvent.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 6 of 17

ModPlus
Enthusiast
Enthusiast

My implementation:

using System;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using ModPlusAPI.Windows;

[PublicAPI]
public class RevitEvent : IExternalEventHandler
{
    private Action _doAction;
    private readonly ExternalEvent _exEvent;

    public RevitEvent()
    {
        _exEvent = ExternalEvent.Create(this);
    }

    public bool IsActionExecuted { get; private set; }

    public void Run(Action doAction)
    {
        _doAction = doAction;
        _exEvent.Raise();
    }

    public void Execute(UIApplication app)
    {
        try
        {
            IsActionExecuted = true;
            _doAction?.Invoke();
            IsActionExecuted = false;
        }
        catch (Exception exception)
        {
            IsActionExecuted = false;
            exception.ShowInExceptionBox();
        }
    }

    public string GetName()
    {
        return "RevitEvent";
    }
}

 

Creating an instance with placement in a static property:

public class App : IExternalApplication
{
    private static UIControlledApplication _uiControlledApplication;

    public static RevitEvent RevitEvent { get; private set; }

    public static UIApplication UiApplication { get; private set; }

    public Result OnStartup(UIControlledApplication application)
    {
        _uiControlledApplication = application;
        _uiControlledApplication.Idling += UiControlledApplicationOnIdling;

        RevitEvent = new RevitEvent();
    }

    private void UiControlledApplicationOnIdling(object sender, IdlingEventArgs e)
    {
        if (sender is UIApplication application && HasTabAndPanel())
        {
            UiApplication = application;
            _uiControlledApplication.Idling -= UiControlledApplicationOnIdling;
        }
    }
}

 

Using from dockable panel:

private void StartCommand(RevitCommandId commandId) => App.RevitEvent.Run(() =>
{
    App.UiApplication.PostCommand(commandId);
});
0 Likes
Message 7 of 17

ricaun
Advisor
Advisor
You should use IExternalEventHandler in the modeless to post the commands. If is working without IExternalEventHandler is by luck.

Did you test to post the ID_SELECT_BY_ID inside a simple IExternalCommand?
Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 8 of 17

ModPlus
Enthusiast
Enthusiast

@ricaun wrote:
You should use IExternalEventHandler in the modeless to post the commands. If is working without IExternalEventHandler is by luck.

Did you test to post the ID_SELECT_BY_ID inside a simple IExternalCommand?

I have already written about this several times above - I tried IExternalEventHandler. The command fails to run only if it is posted from the panel. In all other variants (from a window or from a IExternalCommand) the command runs without any problems

0 Likes
Message 9 of 17

jeremy_tammik
Alumni
Alumni

Your code is very confusing for me. Why are you subscribing to the Idling event? Can you remove that, please to clarify how the interaction with the external event is set up? The Idling event is a completely different animal from the external event, and you make it very confusing for yourself and others if you try to use both at the same time.

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 10 of 17

ricaun
Advisor
Advisor

@ModPlus wrote:

I have already written about this several times above - I tried IExternalEventHandler. The command fails to run only if it is posted from the panel. In all other variants (from a window or from a IExternalCommand) the command runs without any problems


Your code looks right. Maybe IExternalEventHandler does not work with ID_SELECT_BY_ID.

 

Is this one? PostableCommand.SelectById that your are using?

 

PS: your could use some extension to 'convert' UIControlledApplication to UIApplication instead of using the Idling approach. Like: https://github.com/ricaun-io/ricaun.Revit.DI/blob/master/ricaun.Revit.DI/Extensions/UIControlledAppl...

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 11 of 17

ModPlus
Enthusiast
Enthusiast

@jeremy_tammik 

Here the test project (Revit 2024) and video. You can try it by yourself

(view in My Videos)

 

0 Likes
Message 12 of 17

ricaun
Advisor
Advisor

That's how that command works, you need to have a view selected or something.

 

If you click in an dockable panel, could be the default Project Browser the command Select by ID goes to disable.

ricaun_1-1724239995662.png

 

And if you click in the current view, the command is enabled.

ricaun_0-1724239983637.png

 

Here is video, and I tested with the IExternalCommand and does not work when the command Select by ID is disable. That's how post command works. 😀

 

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 13 of 17

ModPlus
Enthusiast
Enthusiast

@ricaun great observation! It all makes sense now. I just have to figure out a solution. I'll try to activate the view...

0 Likes
Message 14 of 17

ModPlus
Enthusiast
Enthusiast

Unfortunately, re-activating the view before posting the command did not work. I even tried moving the focus to the document control, but that didn't work either. Does anyone have any other ideas?

0 Likes
Message 15 of 17

Speed_CAD
Collaborator
Collaborator
Accepted solution

Hi ModPlus,

 

The problem is that the view has no focus, it seems that PostableCommand.SelectById needs to be enabled for it to work.

 

I did this little test just to confirm that focus is needed on the view, with this it works:

test.gif

 

private DispatcherTimer m_Timer;
public ICommand PostSelectByIdCommand => new RelayCommand(() =>
{
    m_Timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
    m_Timer.Tick += Timer_Tick;
    m_Timer.Start();

    try
    {
        App.UiApplication.ActiveUIDocument.Selection.PickPoint(Autodesk.Revit.UI.Selection.ObjectSnapTypes.None);
    }
    catch { }
    finally
    {
        m_Timer.Stop();
        m_Timer.Tick -= Timer_Tick;
    }

    App.RevitEvent.Run(() => SafeExecute(() => 
    {
        App.UiApplication.PostCommand(RevitCommandId.LookupPostableCommandId(PostableCommand.SelectById));
    }));
});
private void Timer_Tick(object sender, EventArgs e)
{
    PressService.PostMessage(ComponentManager.ApplicationWindow, (uint)PressService.KEYBOARD_MSG.WM_KEYDOWN, (uint)Keys.Escape, 0);
}

 

Mauricio Jorquera
0 Likes
Message 16 of 17

ModPlus
Enthusiast
Enthusiast

@Speed_CAD  interesting solution! Thank you! I'll try to use it

0 Likes
Message 17 of 17

jeremy_tammik
Alumni
Alumni

Thank you for the interesting discussion, research and documentation. I shared a short pointer on the blog:

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes