IExternalCommand with WPF UI?

IExternalCommand with WPF UI?

juqing27
Contributor Contributor
3,457 Views
8 Replies
Message 1 of 9

IExternalCommand with WPF UI?

juqing27
Contributor
Contributor

Hi,

 

This question probably has been asked before, but my searching didn't get to a very clear answer so pardon me if this is very old. In the past I've developed Revit Application add-in, which included its own Ribbon, Panel, Button in Revit Environment, and a WPF Page.

 

This time, I have another task that can basically be done by a simple IExternalCommand, and it currently lives just under the Revit Add-In Ribbon, External Tools Panel as another generic button. I love the simplicity of this and don't plan to add Ribbon/Panel/Button unless absolutely necessary. However, to make the function complete, I do need to add a WPF Page to get a few inputs from the User. My questions are,

  1. Can a WPF Page be added to an IExternalCommand ?  An example would be super helpful.
  2. If there is going to be multiple commands involved in the process, does it mean the Add-In have to be an Application, not a Command?
  3. Can an Application live under the Revit Add-In Ribbon, External Tools Panel as a generic button, or does it always need to be associated with its own big button with icon?

Thanks!

0 Likes
Accepted solutions (1)
3,458 Views
8 Replies
Replies (8)
Message 2 of 9

joshua.lumley
Advocate
Advocate

A wpf page can be added easily if it is modal.  I think it is possible to daisy chain commands like: 

 commandData.Application.PostCommand(revitCmdID)

Check out this link:
 https://thebuildingcoder.typepad.com/blog/2013/10/programmatic-custom-add-in-external-command-launch... 

 

Joshua.

Message 3 of 9

RPTHOMAS108
Mentor
Mentor

A WPF window can be shown from any context, just instantiate it and show it, either modal or modeless within the IExternalCommand.

 

If showing modeless then you need to create an ExternalEvent for the controls you activate on it once shown. I usually do this for the session during OnStartUp of IExternalApplication this may not be required i.e. ExternalEvent.Create is a static method however I find no good reason to create it elsewhere (I'd have to keep track of it's creation). Also one ExternalEvent can solve multiple purposes, from looking at journals people tend to create too many of these rather than creating one for all purposes.

 

You don't have to add any UI elements with IExternalApplication you can alternatively use IExternalDBApplication (where no UI manipulation is permitted). 

 

If you are publishing to the exchange store they probably have a requirement for a button rather than adding under 'External Tools', see guidance for that.

Message 4 of 9

juqing27
Contributor
Contributor

Thanks @RPTHOMAS108 ! I wasn't thinking much about modal vs. modeless previously. But now I'm leaning more on modeless (not exactly sure why so I may start a new post in the future to understand the difference:). Can you point me to an example with IExternalCommand/ExternalEvent to show WPF Page as DockablePane modeless? Or is that scenario too specific...?

0 Likes
Message 5 of 9

RPTHOMAS108
Mentor
Mentor
Accepted solution

You can see examples within SDK:

 

...\Samples\DockableDialogs

...\Samples\ModelessDialog\ModelessForm_ExternalEvent

...\Samples\PostCommandWorkflow\CS

 

Modeless would be most peoples preference but we should ask ourselves if the user needs it to be ever present and live between other interactions with Revit.

 

If you show five buttons on a window that the user can access through a shortcut key they've set up then perhaps the answer is probably modal. The interesting thing about shortcut keys is that the first key can show your window (would require a button) then next key can be bound (in your code) to a certain control on your window. So when your window has focus the next key will activate the required control. i.e. you can extend the range of shortcut keys for a certain shortcut prefix.

 

If you are showing live results or can't predict how and when the user may want to access what is in the window such as dropping content from it then probably the answer is modeless.

 

Either way modeless is more complicated as you often have to set up events to monitor the changing Revit application state whereas modal is simple because the application state is fixed at the point you show the window e.g. you know when you read properties such as ActiveUIDocument they are dependable unless your process has (directly/indirectly) changed the state since.

0 Likes
Message 6 of 9

juqing27
Contributor
Contributor

Thanks again @RPTHOMAS108. I find your explanation for modeless/modal very helpful for me - actually more than my original questions :). So if you don't mind, I'm adding on a couple questions although they are a bit off-topic for this post:

  • My process may take long. To be more specific it reads the parameters and geometry of each room in the project, and writes the data out to a file. For the size of our project and quantity of rooms, my estimation is 3-5 min - from the user hits the big "run" button (please imagine there is this button...:) to when the process is completed.
    • If the add-in is modal, will the add-in dialog and all the rest of Revit freeze during this 3-5 min?
    • If the add-in is modeless,  will it make a difference (user can interact with Revit as normal while the process runs in the "background")?

Based on your point on modeless/modal, I believe modal fits better for my current task. However if modal/modeless matters in this case, I do want to make it a better experience for the users, rather than let them staring at the frozen screen and praying for response.

 

Thanks!

0 Likes
Message 7 of 9

RPTHOMAS108
Mentor
Mentor

In truth a combination of both would be required for a long running process where you want to show to the user the system hasn't become unresponsive. As soon as you close the modal window the code moves onto the next stage so you would typically treat it in stages:

<Main code>

Create the window and ShowDialog

<Button click event handler>

Upon button activation (clicked event handler) you then update your settings object from options picked in the window

Set a boolean flag to confirm the ok button was pressed (not cancel) in the Window

Close the window

<Button click event handler end>

<Back in main code>

Check flag above to see if you proceed or not

Read settings from window object to pass onto procedure

Create and show modeless window for status bar

Run procedure with settings

...

During each procedure cycle check a status flag to see if user pressed cancel on status bar window. You can't do this for a long running Revit task, you can break down such a task into parts i.e. once you instruct Revit to do something you can't interact with it until it is done. There is the progress changed event you can subscribe to but that only reports what is in the status bar Revit inherently shows. This also allows cancel at certain Revit designated stages.

...

Close modeless status bar window

<Main code end>

 

The main point for status bar windows is that they will not update while Revit is doing something (the message queue becomes blocked). You have to force them to periodically process the messages by calling system.windows.forms.application.DoEvents etc. This works for both WPF and Forms, forms may inherantly do this I don't know.

 

 

0 Likes
Message 8 of 9

juqing27
Contributor
Contributor

Hello @RPTHOMAS108, and thanks for your patience! 

I'm trying this one from SDK: ...\Samples\ModelessDialog\ModelessForm_ExternalEvent

However, when I debug it from Visual Studio with Revit 2021, there's nothing happening in Revit as far as I can tell. I then copied the code over to a standalone little project, created an .addin file, and tried debugging again, yet there's still no button under "Add-Ins" Ribbon, "External Tools" Panel (this is where I'd expect, but maybe it ends up not here?) that shows the Form with click. 

 

Below is my .addin file in case it helps diagnosing- 

 

<?xml version="1.0" encoding="utf-8" standalone="no"?>
    <RevitAddIns>
        <AddIn Type="Application">
            <Name>test_ReadRoomInfo</Name>
            <Assembly>RVT_ReadRoomInfo.dll</Assembly>
            <AddInId>082F4D4A-6568-44CB-86DE-7468D60CFA9C</AddInId>
            <FullClassName>RVT_ReadRoomInfo.Application</FullClassName>
            <VendorId>test</VendorId>
    </AddIn>
</RevitAddIns>

 

Any clues? Thank you.

0 Likes
Message 9 of 9

RPTHOMAS108
Mentor
Mentor

I'm not overly familiar with those samples, so probably not the best person to ask. Can only recommend reviewing the usual suspects: class name is correct, assembly location, manifest contents etc. Typically you get error messages if the manifest contains incorrect paths etc.

 

As far as I can tell the IExternalApplication implementation for that does not add anything to the UI. The IExternalCommand implementation shows the form and there is an IExternalCommand section you are missing in the addin manifest above for that.