How to use multiple Revit-buttons and pages with one WPF window

How to use multiple Revit-buttons and pages with one WPF window

samuel_kreuz
Contributor Contributor
1,323 Views
7 Replies
Message 1 of 8

How to use multiple Revit-buttons and pages with one WPF window

samuel_kreuz
Contributor
Contributor

Hello,

 

I am new to the forum so hello to everyone 🙂

 

I have some issues implementing a seemingly simple task using the Revit API.

 

I would like to have two or more buttons in the Revit UI, which both open the same WPF window, but different pages, depending on which button was clicked.

 

At the time my code looks like this:
The buttons are created at the start of the application. Each of them calls which has the "IExternalCommand" interface. However if I create a new WPFWindow in each of them, I have two windows, not one.

 

Do you have any suggestions on which might be the best structure to implement this to only open one window and use this one window for all further pages? Where in the project should I declare and initialize the WPF window so it is accessible from both commands?

 

A link to a project which uses similar structures might be enought to help me.

 

Thanks 🙂

 

Apps.cs:

 public Result OnStartup(UIControlledApplication a)
        {
            string path = Assembly.GetExecutingAssembly().Location;

            //Create Tab
            a.CreateRibbonTab("MyButtons");

            //Panel
            Autodesk.Revit.UI.RibbonPanel revitTools = a.CreateRibbonPanel("MyButtons", "MyPanel");

            PushButtonData button1data = new PushButtonData("Button 1", "This is button one", path, "WPFWindowProblem.CommandButton1");
            PushButton button1 = revitTools.AddItem(button1data) as PushButton;
            button1.ToolTip = "This is button one";

            PushButtonData button2data = new PushButtonData("Button 2", "This is button two", path, "WPFWindowProblem.CommandButton2");
            PushButton button2 = revitTools.AddItem(button2data) as PushButton;
            button2.ToolTip = "This is button two";

            return Result.Succeeded;
        }

 

CommandButton1.cs:

 public class CommandButton1 : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Application app = uiapp.Application;
            Document doc = uidoc.Document;

            TaskDialog.Show("Info", "Button one was clicked");
            
            WPFWindow window = new WPFWindow();
            Page01 page1 = new Page01();

            window.Content = page1;
            window.Show();

            return Result.Succeeded;
        }
    }

 

CommandButton2.cs

 public class CommandButton2 : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Application app = uiapp.Application;
            Document doc = uidoc.Document;

            TaskDialog.Show("Info", "Button two was clicked");

            WPFWindow window = new WPFWindow();
            Page02 page2 = new Page02();

            window.Content = page2;
            window.Show();

            return Result.Succeeded;
        }
    }

 

Revit 2020, .NET 4.7, VS2019, C'#

0 Likes
Accepted solutions (2)
1,324 Views
7 Replies
Replies (7)
Message 2 of 8

samuel_kreuz
Contributor
Contributor

 I think I finally found a solution by myself:

 

When the window-class is declared as a static object in the App.cs, it can be accessed from everywhere.

 

public static WPFWindow window;

 

so now I can just access it like this from the Command scripts:

 

  if (App.window != null)
            {
                App.window.Activate();
            }
            else
            {
                App.window = new WPFWindow();
            }

            App.window.Content = page1;
            App.window.Show();

 

0 Likes
Message 3 of 8

RPTHOMAS108
Mentor
Mentor
Accepted solution

That is a strange approach to be honest (setting the content of the window in code) what do you see as it's advantages?

 

In answer to your question you have two Windows because you created two instances with the 'new' keyword but fixing that will not be the end of your problems. 

 

If you use Windows.Show (modeless) instead of ShowDialog (modal) then you should be implementing IExternalEventHandler. Currently the objects from Revit will go out of context as soon as your window is shown and you'll therefore not be able to interact with them in a meaningful way.

 

I would review the samples in the SDK regarding modeless windows to see if any of those fit with what you want to achieve. Would also not go down the route of using the application framework in an add-in. Since the application in that instance is Revit and not an exe you are creating yourself (you may find it leads to unstable results).

Message 4 of 8

jeremy_tammik
Alumni
Alumni
Accepted solution

Just as you have discovered, each button is associated with a different external command. 

  

You might consider implementing those two separate external command to launch the same form and pass in different arguments to it, so it can tell which command is the active one and adapt accordingly.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 5 of 8

samuel_kreuz
Contributor
Contributor

Thanks for the input!

 

The idea was to later implement a WebView2 interface which cahnges the web-URL depending on which button you click. I also discovered that it probably is not necessary to create two WPF pages and rather just create one page and change the change source URL.

 

I looked at the SDK files and as I understood, External Events are only necessary when interacting with Revit itself, like changing something inside a project. So my guess was that opening an WPF window doesn't need an external event, only if I plan to send data back to Revit, is that correct?

0 Likes
Message 6 of 8

samuel_kreuz
Contributor
Contributor

Thanks 🙂 I will try that! Is there a way to pass information/objects directly to an IExternalCommand when calling it/the button is pushed? Further if thats not the case I guess it is aslo necessary to create one class for each Button in the Revit interface?

 

 

0 Likes
Message 7 of 8

jeremy_tammik
Alumni
Alumni

> Is there a way to pass information/objects directly to an IExternalCommand when calling it/the button is pushed?

 

No.

 

> I guess it is necessary to create one class for each Button in the Revit interface?

 

Yes.

  

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

samuel_kreuz
Contributor
Contributor
great, thanks! 🙂
0 Likes