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;
     ...
}
7 Comments

Leave a Reply to sc Cancel reply

Your email address will not be published. Required fields are marked *