Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Revit API with WPF

15 REPLIES 15
SOLVED
Reply
Message 1 of 16
Rubens1234
11222 Views, 15 Replies

Revit API with WPF

Hello everyone!

 

I've exported Revit functions to WPF using event handlers, etc.

 

I'm using the WPF Window (instead of User Control or Page) and to show my content I use the expression window.Showdialog();

 

The problem I'm having now is that my code only runs after the window is closed. What should I do in order to run the code while the window is opened? Or should I use other type of WPF interfaces (as Page)? I've tried most things that I recall and still did not managed to get around this problem.

 

Thank you all!

Tags (2)
15 REPLIES 15
Message 2 of 16
L.Maas
in reply to: Rubens1234

This post is better suited in the Revit API forum. (I have asked moderator to move this thread)

 

Not too familiar with WPF myself. But I have read that there were some issues with WPF.

See the blog of the buildingcoder ( @jeremytammik), good source regarding the Revit API. He also visits the Revit API forum on a regular basis.

Louis

EESignature

Please mention Revit version, especially when uploading Revit files.

Message 3 of 16
jeremytammik
in reply to: Rubens1234

I don't use WPF myself.

 

Here are some hints that cropped up two years ago:

 

http://thebuildingcoder.typepad.com/blog/2016/01/devday-conference-in-munich-and-wpf-doevents.html

 

More WPF discussions are tagged in The Building Coder WPF category:

 

http://thebuildingcoder.typepad.com/blog/wpf

 

You should also check for WPF related threads here in the Revit API discussion forum.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 16
Rubens1234
in reply to: jeremytammik

Thank you very much for the answer.

 

So, if I want to develop an API but using a different UI, which one would be indicated? For instance, what type of UI do Revit developers use?

 

Thanks!

Message 5 of 16
newbieng
in reply to: Rubens1234

Hi,

I use WPF in Revit and I generally set them up as Page. That way I managed to run different actions in Revit while the WPF is active. You may try to set on Page and see if it helps you.

 

Anyway, I used Jeremy's links as a guide to build my WPF code, have a look at them I'm sure they are helpful to you.

 

Bye

 

Message 6 of 16
r.singleton
in reply to: newbieng

I went the Window route in WPF with Revit AddIns for awhile.

 

You have to use WindowName.Show() for modeless, not WindowName.ShowDialog() which is modal, if I understand your question correctly.

 

 

Message 7 of 16
Rubens1234
in reply to: r.singleton

Thanks a lot! That helped me solve some of my problems.

 

However, when I hide my first Window and open a 2nd one, if I try to run another Revit API function it seems it doesn't read the information in the external events, as it throws this exception: "object reference not set to an instance of an object". How did you got around that problem? Even if I create all the external events in the main class, it always throws that exception when I close/hide the first Window.

 

Thank you all!

Message 8 of 16
r.singleton
in reply to: Rubens1234

This is where you start to get into that blurred line between "am I struggling with the Revit API, or am I struggling with a more fundamental concept in .NET development." Or, you could say "Is there where I start looking for help at Stack Overflow", lol.

 

https://stackoverflow.com/questions/3827612/how-to-make-my-wpf-mainwindow-a-singleton

 

I'm pretty sure I've been where you are, and I think easy to steer you back to a good working point, so I won't concern myself with that at the moment. Plus, given my name, I feel it's appropriate to discuss singletons with you.

 

You've created a new instance of your Window class, and some of the references are now probably new or wiped out. Text in text boxes get wiped out, items in datagrids, etc. There's probably some data item that is a "new instance" and so the API doesn't see a value in it, where it would if it were simply looking at your first instance of the window.

 

You want your window to be the one and only instance of it to ever exist during that session, I would imagine. And "close" would really just "hide" and "show" will only instantiate it one time. After, it just unhides it. This will also be handy because your API functions can actually map directly into the elements in your Window, which would be harder if you had to tell it how to find the current instance of the Window every stinking time.

 

I hope this helps you get started at least, and that I have not addressed your question incorrectly.

 

 

Message 9 of 16
Rubens1234
in reply to: r.singleton

Thank you for your trouble r.singleton, I really appreciate it! Let me just begin by saying that I'm sorry for all the questions that I will ask you (bellow). I've read and re-read and re-re-read your post and StackOverFlow posts and still don't get it.

 

For starters, I don't understand the concept behind the singleton Window. Are you saying that, instead of opening/closing several windows, I should only have one window (the singleton) and update it accordingly?

 

For instance, according to your link I've added 

public partial class MainWindow : Window
{
public static MainWindow Instance { get; private set; }

static MainWindow ()
{
Instance = new MainWindow ();
}
private MainWindow ()
{
 //...
}

//...where I've added all the buttons and event raises
//Then, I have the Next button, that will close the MainWindow and open the SecondWindow

private void NextButton_Click(object sender, RoutedEventArgs e)
        {
            SecondWindow secondWindow = new SecondWindow();
            Window window2 = new Window();
            window2.Content = secondWindow.Content;

            //And then I hide the window (current one) while I just opened the new one previously
            var button = sender as System.Windows.Controls.Button;
            var win = Window.GetWindow(button); win.Hide();

            //Show the new window (i.e. Window:SecondWindow)
            window2.Show();
        }
}

 

But then, on the StackOverFlow it was also stated that I should add this:

public App()
{
    ...
    Startup += App_Startup;
    ...
}

void App_Startup(object sender, StartupEventArgs e)
{
    TestApp.MainWindow.Instance.Show();
}

The thing is that I don't have an Application but a Window instead, and the Window doesn't have the Startup function.

 

So how should I "transform" my MainWindow into a singleton? And how should I "replace" the buttons of the MainWindow with the buttons on the SecondWindow without actually closing the MainWindow? This might sound a little bit confused.

 

Basicaly, what I'm saying is how should I design my code in order to 1) Open MainWindow and use the buttons in it, 2) Click on button "Next" so that I will go to SecondWindow, and 3) On the SecondWindow I visualize other buttons and I can actually use them.

 

Thank you very very much!

 

 

 

 

Message 10 of 16
r.singleton
in reply to: Rubens1234

Rubens, I apologize, I  misunderstood what you meant by "second window" in my prior response. 

 

Judging by your code, your window is already a singleton. It's static and declares an Instance for use. Oops!

 

You are actually having an issue with another window entirely, and it's nullifying something in your code. To diagnose that, I'd have to see the structure of your work. I can check into it privately if that doesn't concern you. I'd only ask that I be able to share findings in a whittled down example to help future readers of this thread. And also so that Jeremy can correct my advice further, lol.

 

It's admittedly a bit of a chore handling external events unless you study the DockableDialog and Modeless dialog examples in the SDK and take special note of how they instantiate one, then swap out their functions in context.

 

You might have already studied those samples. Awesome if you did. If you didn't, go do it. The way the external events are handled there will be invaluable to future proofing your work and keeping it manageable. 

 

If that didn't make sense, as I tend not to, may I please ask this question: If you are using WPF, it sounds to me like a TabControl with TabItems inside your already functional Window might help you to achieve something similar. And they are incredibly easy to use, and this way you might have the choice to meet your deadlines and later start picking apart this null reference.

Message 11 of 16
newbieng
in reply to: r.singleton

Hi guys,

sometimes you don't need to use a modeless form, that requires ExternalEvents to be run. I developed an app with multiple WPF pages using only modal forms; naturally I can't "focus" on Revit while the WPF if open, but I can run commands clicking some buttons in my wpf.

This way was easier for me.

 

I would be interested to understand your modeless case with multiple windows, if @r.singleton expose your example.

 

 

 

Message 12 of 16
Rubens1234
in reply to: r.singleton

Ok everyone! 

 

First of all thank you r.singleton! I had already seen the Modeless dialog example from SDK but it was (still is) too complex for me. My background is civil engineering and only started to lean (novice) c# programming at 6 months ago, Revit API 4 months ago and WPF 2 months ago, so I still don't understand a lot of the used concepts and functions in computer science etc.

 

So, I've figured a way to open External Events in 2nd, 3rd, 4th windows, but it probably might be not the best way to do it. Anyhow, it works for me (at least so far so good).

 

Before, I was using my MainClass (where I start the IExternalCommand or IExternalApplication) to create my External Events and Handlers, and then saved them and exported to each Window. For instance

//MainClass
            ExternalEvent externalEvent1;
            EventRegisterHandler_DoSomething handler1= new EventRegisterHandler_DoSomething ();
            externalEvent1= ExternalEvent.Create(handler1);
            Window1.ExternalEvent_1= externalEvent1;

            ExternalEvent externalEvent2;
            EventRegisterHandler_DoSomething handler2= new EventRegisterHandler_DoSomething ();
            externalEvent2= ExternalEvent.Create(handler2);
            Window2.ExternalEvent_2= externalEvent2;


// -----------In Window 1.cs-----------------
public ExternalEvent ExternalEvent_1 { get; set; } //get the event from MainClass

// ------------In Window 2.cs--------------
public ExternalEvent ExternalEvent_2 { get; set; } //get the event from MainClass as well

 

But this throws a "event equals null" exception at Window 2.

 

So, after analyzing again the Modeless dialog I tried this:

//MainClass
            ExternalEvent externalEvent1;
            EventRegisterHandler_DoSomething handler1= new EventRegisterHandler_DoSomething ();
            externalEvent1= ExternalEvent.Create(handler1);
            Window1.ExternalEvent_1= externalEvent1;

            ExternalEvent externalEvent2;
            EventRegisterHandler_DoSomething handler2= new EventRegisterHandler_DoSomething ();
            externalEvent2= ExternalEvent.Create(handler2);
            Window1.ExternalEvent_2= externalEvent2;


// -----------In Window 1.cs-----------------
public ExternalEvent ExternalEvent_1 { get; set; } //get the event from MainClass
public ExternalEvent ExternalEvent_2 { get; set; } //get the event from MainClass as well

private void NextButton_Click(object sender, RoutedEventArgs e)
        {
           Window2 nextWindow = new Window2 (ExternalEvent_2);
            Window window = new Window();
            window.Content = nextWindow.Content;

            (...) basically making sure the format/properties are correct
           
            //And then I hide the Window1 (current one) 
            var button = sender as System.Windows.Controls.Button;
            var win = Window.GetWindow(button); win.Hide();

            //Show the new window 
            window.Show();
        }

// ------------In Window 2.cs--------------
public ExternalEvent ExternalEvent_2;

public Window 2(ExternalEvent externalEvent_2)
        {
            InitializeComponent();
            ExternalEvent_2= externalEvent_2
        }

        private void ButtonThatWillRaiseTheEvent(object sender, RoutedEventArgs e)
        {
            ExternalEvent_2.Raise();
        }

Basically, I create all the external Events I'm going to need in my MainClass and then my Window1 will "distribute" them. So, if i have 3 windows each with one external event, I need to get all three in Window 1, pass two of them to Window2 and then Window2 will pass the last one to Window3. Perhaps this was obvious or perhaps it will create other problems in the end. I don't know yet. Just trying to help curious minds 🙂 

Message 13 of 16
r.singleton
in reply to: Rubens1234

newbieng,

That is sage advice. I've been taking inventory over the last few weeks and fettering out which situations really needed a modeless dialog. Modal drastically simplifies things in the situations where it can be used. Once I learned how to get modeless working, hindsight proved that I got carried away with it.

 

Rubens,

Anything you define in the class for Window1 will be contained in Window1. But if what you are writing is also contained in Window1 (as appears to be the case here), you actually wouldn't need to type Window1.whatever since it's implied. Here's an example.

 

So:

ExternalEvent externalEvent1;
            EventRegisterHandler_DoSomething handler1= new EventRegisterHandler_DoSomething ();
            externalEvent1= ExternalEvent.Create(handler1);
            Window1.ExternalEvent_1= externalEvent1;

            ExternalEvent externalEvent2;
            EventRegisterHandler_DoSomething handler2= new EventRegisterHandler_DoSomething ();
            externalEvent2= ExternalEvent.Create(handler2);
            Window1.ExternalEvent_2= externalEvent2;

This code could look like this

 

ExternalEvent externalEvent1;
            EventRegisterHandler_DoSomething handler1= new EventRegisterHandler_DoSomething ();
            externalEvent1= ExternalEvent.Create(handler1);
            ExternalEvent_1= externalEvent1;

            ExternalEvent externalEvent2;
            EventRegisterHandler_DoSomething handler2= new EventRegisterHandler_DoSomething ();
            externalEvent2= ExternalEvent.Create(handler2);
            ExternalEvent_2= externalEvent2;

No big deal though. What you have will work, and you'll get to that point where the Modeless example will make enough sense to put to use someday.

 

My background is sheet metal. I can sympathize with your background.

Message 14 of 16
Hazem.Elamir
in reply to: r.singleton

hi dear

i can't use transaction.start(); when open the wpf window using wpf.show(); instead of wpf.showdialog();

how can we solve that?

Message 15 of 16
jeremytammik
in reply to: Hazem.Elamir

When you use Show instead of SHowDialog, you leave the main thread and enter a modeless context.

 

In a modeless context, you cannot use the Revit API at all:

 

http://thebuildingcoder.typepad.com/blog/2015/12/external-event-and-10-year-forum-anniversary.html#2

 

A workaround is possible.

 

In that case, you need to redesign your application pretty fundamentally:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.28

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 16 of 16
r.singleton
in reply to: Hazem.Elamir

To add my experience to what Jeremy said (you need External Events to make your transactions work modelessly), I have built a dockable dialog with WPF that used a lot of external events. Autodesk recommends that you try to use modal dialogs as much as you can instead of modeless. It will make your project much easier.

 

However, I can tell you that a fully functional suite of tools with external events is possible. But you need to be sure that the added complexity is worth it. 

 

If you are going to have multiple external events, I strongly recommend that you look at the Revit SDK sample for External Events specifically. It shows how to use enums and a handler to swap out external events so that you do not have a sloppy mess of external events in your application.

 

You can also have a modeless dialog that, when you press a button to start a transaction, will open a modal window (progress bar or some other window), and that window will start the transaction instead. Once done, it closes, and the user feels like they are using a modeless context. A system like that would be much easier to maintain.

 

I hope this helps.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community