Essential items for creating a WPF window for Revit API plugins (Revit API+WPF Series 2/3)

In this second part of the series, we will discuss essential items to apply when creating WPF windows.

In order to proceed, make sure your plugin project is capable of creating WPF windows. If not, you better create a project template with WPF functionality and create another new project. Then create a WPF window. In this case, make a window and name it viewThisWindow.xaml.

To make a WPF window work on its stable condition inside Revit, the following minimum recommendations are to be followed.

The window must be disposable

WPF windows are not disposable by default, so I recommend every WPF window be disposable. To do that, in the viewThisWindow.xaml.cs, set this on the line of the class declaration:

public partial class viewThisWindow : Window, IDisposable

 

The IDisposable interface will enable us to use the using() statement later on.
Implement the IDisposable interface to be able to create the Dispose() function inside the class, then input this line inside the function:

public void Dispose()
{
     this.Close();
}

 

This will force the window to close when the using() statement has done its work, especially when the operation is cancelled.

 

The window does not need maximize and minimize buttons

When working on a modal window, for most instances, it must only have an executing button (i.e. OK button) and a canceling button (i.e. Exit button on the upper right hand side of the window), and the minimize and maximize buttons are disabled.

Create a button inside the window and label it “OK”. Then make this button the default button by ticking the button’s IsDefault property from the properties window.

Addendum: After creating the OK button, double-click it to create an event, and in the event function that was created (somewhat like Button1_Click()), insert this line:

this.DialogResult = true;

This will help closing the window and tell the program that the window is successfully closed.

To disable the minimize and maximize buttons, first copy the following code and paste it inside the viewThisWindow class above the viewThisWindow() constructor.

[DllImport("user32.dll")]
internal extern static int SetWindowLong(IntPtr hwnd, int index, int value);

[DllImport("user32.dll")]
internal extern static int GetWindowLong(IntPtr hwnd, int index);

internal static void HideMinimizeAndMaximizeButtons(Window window)
{
      const int GWL_STYLE = -16;

      IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
      long value = GetWindowLong(hwnd, GWL_STYLE);

      SetWindowLong(hwnd, GWL_STYLE, (int)(value & -131073 & -65537));
}

 

Then in order for this to work, create a SourceInitialized() event function.
To create this event function, select first the window itself in the XAML design window, then in the Properties window, click on the Event Handler window (the lightning-like icon located on the upper right). Go to the SourceInitialized event and double-click on the space adjacent to it. It will automatically create an event function with the _SourceInitialized inside the .xaml.cs file.

Inside this function insert the following line:

HideMinimizeAndMaximizeButtons(this);

 

This will hide the minimize and maximize buttons before the window shows up on the screen.

Note: There is another event function that is Loaded() but SourceInitialized() event function runs in between the constructor and the Loaded() function. Loaded() is dedicated to other things when the window is loaded and it is not the best location to execute HideMinimizeAndMaximizeButtons().

 

It must be always on top of the Revit application

To be able to be on top of the Revit application, we must also insert these lines to our plugin class:

using (viewThisWindow view = new viewThisWindow())
 {
      // the following two lines let the Revit application be this window's owner, so that it will always be on top of the Revit screen even when
      // the user tries to switch the current screen.
      System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);
      helper.Owner = proc .MainWindowHandle;
     ...
 }

 

Notice that we just used the using() method here. As you all know, using() can only be used when the class that you want to create is disposable; that is, the class is derived from the IDisposable interface and there is a Dispose() method inside that class. Anything done inside the Dispose() function of the class will be executed once using() has done its job.

And as an option, we un-tick the ShowInTaskbar property so that the icon of this window won’t show up in the taskbar.

Alternatively, you may just use these lines when you are loading just one WPF window inside your plug-in.

// System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();

using (viewThisWindow view = new viewThisWindow())
 {
      System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view);
      helper.Owner = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
     ...
}

6 comments

    • umadzkun

      Sorry to keep you waited for too long. I’d just posted Part 3, but it is still missing some contents. I’ll make update for it so please wait for more. In the meantime, take a time on my new post.
      Thank you very much!

  1. Mathias

    Hi! Great blog series!

    I don’t quite understand where to put the code for the window to be on top of the revit window:
    “To be able to be on top of the Revit application, we must also insert these lines to our plugin class”
    Should this code be inserted in the Window_SourceInitialized method?

    Thanks!

    • umadzkun

      Hi there, Mathias.

      Thank you for the inquiry. I am sorry if that was not clear to you.
      The code is to be inside the Execute() function itself. Whenever you want to call the WPF window the handle protocol must be called before creating a WPF window. You do not need to repeat this even if you need to call more than one WPF window during a command execution.
      On the other hand, what needs to be put inside Window_SourceInitialized is HideMinimizeAndMaximizeButtons(this).

  2. Nam

    Hi umadzkun,
    It is clear that you spent time and effort to explained the steps by pictures and words. by the way it is still very confusing with a totally newbie like me (such as i dont know exactly which lines I need to copy to where). So could you mind emailing me the file you just setup ? thanks you so much and have a good day 🙂
    pls email : truongbaolam0209@gmail.com

    • umadzkun

      Hi there, Nam.
      Sorry for the tremendously late reply.
      Have you solved it while you were waiting? I can give you the setup if you haven’t done it yet so just wait.

Leave a comment