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: 

WPF window loses control when Revit API displays an error message

8 REPLIES 8
Reply
Message 1 of 9
jeroenvanvlierden
2248 Views, 8 Replies

WPF window loses control when Revit API displays an error message

Hello,

 

I have created a testapp with a WPF window, with a button, I create the Window from the command class as follows

This is tested in Revit 2014, but will also occur in other versions I am sure...

 

public Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
{
Window1 t = new Window1(commandData);
t.ShowDialog();
return Result.Succeeded;
}

 

and which contains the following code in a button click event handler

 

Autodesk.Revit.DB.Family f;
string p1 = @"C:\temp\testje\family.rfa";
Autodesk.Revit.DB.Transaction t = new Autodesk.Revit.DB.Transaction(_comm.Application.ActiveUIDocument.Document);
t.Start("loadf");
_comm.Application.ActiveUIDocument.Document.LoadFamily(p1, out f);
t.Commit();

 

if the family loads correctly this is no problem: the plugin stays in control

 

but if for instance we change the family to a family from a later version of Revit, Revit will tell us that this is the case (which is fine by me, allthough I would prefer no dialogues in my API code)

 

after this, all of a sudden, the Revit UI is back in control, even though the WPF Window is still active??

In Winforms this would not be the case, a modal dialogue is always modal

 

Anyone has an idea what is the issue here and how to deal with it?

 

 

 

8 REPLIES 8
Message 2 of 9
ollikat
in reply to: jeroenvanvlierden

Hi

I would suspect that exception was thrown somewhere. Usually when .NET exception occurs during the Revit add-in, Revit catches it and tells it in the UI (I'm sure you are familiar with that message dialog). But sometimes for some reason (that at least I'm not aware) a thrown exception is somehow totally ignored and forgotten. In other words you wont get any kind of message about the exception. Your code just has stops executing and Revit receives the control again.

Fortunately Visual Studio provides an easy way of observing exceptions during run time. Just press Ctrl + Alt + E and you will see the dialog called "Exceptions". You need to check the "Thrown" check box for the "Common Language Runtime Exceptions". Now when you have attached the visual studio debugger into Revit process you will be notified for every single .NET exception that happens, even it would be handled by try-catch block. This might help you to trace the problem.
Message 3 of 9
jeroenvanvlierden
in reply to: ollikat

Hello,

thanks for getting back to me.I already had the CLR exceptions box ticke in
the debug environment, and noticed there was none.

In this case I have written a very small client that simply loads a family
which is created in a newer version of Revit than the version that is
running. This causes a messagebox from Revit that displays this mistake.

My point is: the behaviour is different in a Winforms plugin, compared to a
WPF plugin. The 'code behind' is exactly the same between the two clients.

This can easily be reproduced by creating an IExternalCommand derived class
which opens a window with a single button and a click handler. In the click
handler do the loadfamily call that causes Revit to display the message and
you will see the difference.

best regards
Jeroen
Message 4 of 9
ollikat
in reply to: jeroenvanvlierden

Hi

Yes...indeed I was also able to reproduce the exact behavior you reported.

Documentation of ShowDialog() says:

"A window that is opened by calling the ShowDialog method does not automatically have a relationship with the window that opened it"

 

So I though maybe assigning a owner window would solve the case. I tried the following:

 

var wnd = new Window1(commandData.Application.ActiveUIDocument.Document);

Process[] processes = Process.GetProcessesByName("Revit");

if (0 < processes.Length)
{
  IntPtr h = processes[0].MainWindowHandle;
  var wih = new System.Windows.Interop.WindowInteropHelper(wnd);
  wih.Owner = h;
}

wnd.ShowDialog();

 

But it didn't help.

 

So...I'm also a bit out of ideas. I agree that the window should not behave like that...it should keep it's modality.

 

Message 5 of 9
jeroenvanvlierden
in reply to: ollikat

Hi thanks again

 

 

I consider this as quite a big issue. What might work (as a workaround) is creating a winforms window, redefining the WPF form to a WPF control and then host this control from within the Winform window

 

it is a ****ty solution, which makes me wonder why nobody before me has reported this

 

Someone should be able to answer this.... and find a solutionn

Message 6 of 9

http://tech.pro/tutorial/799/wpf-tutorial-using-wpf-in-winforms

 

describes a technique on how to host a WPF usercontrol from a Winforms form

I have used that as a workaround. The workaround works, but it is not a great solution. It will have to do for the time being I am afraid. Hope Autodesk will come up with a better solution. Will ask through ADN next week

 

 

Message 7 of 9
kristu
in reply to: jeroenvanvlierden

I think you need to set the Window.Owner property so it sticks to Revits ApplicationWindow.

 

I've written a class that implements System.Windows.Window for helping me to host WPF UserControl in my API Add-ins for Revit.

 

I use this code to get Revit ApplicationWindow and set it to my class Owner property.

/// <summary>
/// Set this windows owner to Revit ApplicationWindow
/// </summary>
private void SetWindowOwner()
{
      WindowInteropHelper helper = new WindowInteropHelper(this);
      helper.Owner = Autodesk.Windows.ComponentManager.ApplicationWindow;
}

My complete class:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;

namespace MyNamespace
{
    public class UserControlHostWindow : Window
    {

        public string FocusedControlName { get; set; }
        public string WindowPrefixTitle { get; set; }

        /// <summary>
        /// A window for hosting UserControl objects
        /// </summary>
        /// <param name="windowContent">UserControl instance for the windows content</param>
        /// <param name="title">Set the windows title</param>
        public UserControlHostWindow(UserControl windowContent, string title)
        {
            SetWindowOwner();
            WindowStartupLocation = WindowStartupLocation.CenterOwner;

            Content = windowContent;
            SizeToContent = SizeToContent.WidthAndHeight;
            
            WindowPrefixTitle = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
            Title = WindowPrefixTitle + " - " + title;

            Loaded += new RoutedEventHandler(UserControlHostWindow_Loaded);
        }
        
        private void UserControlHostWindow_Loaded(object sender, RoutedEventArgs e)
        {
            if (FocusedControlName != null)
            {
                object c = FindName(FocusedControlName);
                Control cn = c as Control;
                cn.Focus();
            }

            MinWindowSize();

        }
        
        /// <summary>
        /// Set this windows owner to Revit ApplicationWindow
        /// </summary>
        private void SetWindowOwner()
        {
            WindowInteropHelper helper = new WindowInteropHelper(this);
            helper.Owner = Autodesk.Windows.ComponentManager.ApplicationWindow;
        }

        /// <summary>
        /// Set min height and width based on content
        /// </summary>
        private void MinWindowSize()
        {
            if (Content is UserControl)
            {
                UserControl content = Content as UserControl;

                double Border = (ActualWidth - content.MinWidth) / 2;
                double TitlebarHeight = ActualHeight - content.MinHeight - 2 * Border;

                MinHeight = Border + TitlebarHeight + content.MinHeight + Border;
                MinWidth = Border + content.MinWidth + Border;
            }
        }

    }
}

I'm still developing this class so if anyone know of anything else a class like this needs please leave feedback 🙂

 

Message 8 of 9

I tried with an ElementHost in a wpf window but I still have the problem of loss of focus

 

FormElementHost : (lose focus)

 

 - Command :

 

 [Transaction(TransactionMode.Manual)]
    public class FormElementHostCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            FormElementHost form = new FormElementHost(commandData);

            form.ShowDialog();

            return Result.Succeeded;
        }
    }

 - Form (System.Windows.Forms.Form) :

 

  public partial class FormElementHost : Forms.Form
    {
        ExternalCommandData _commandData;

        public FormElementHost(ExternalCommandData commandData)
            : this()
        {
            _commandData = commandData;
        }

        public FormElementHost()
        {
            InitializeComponent();
            this.userControl11.ClickEvent += UserControl11_ClickEvent;
        }

        private void UserControl11_ClickEvent(object sender, EventArgs e)
        {
            Family family;
            string message;
            UtilsFamily.LoadFamily(_commandData.Application.ActiveUIDocument.Document, out family, out message);
        }
    }

 

- UserControl WPF (System.Windows.Controls.UserControl) :

 

public partial class UserControl1 : UserControl
    {
        public event EventHandler ClickEvent;

        private void RaisesEventClick()
        {
            if (this.ClickEvent != null)
                this.ClickEvent(this, new EventArgs());
        }

        public UserControl1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            RaisesEventClick();
        }
    }

 

 

 

 

FormNoElementHost : (not lose focus)

 - Command :

 

 [Transaction(TransactionMode.Manual)]
    public class FormNoElementHostCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            FormNoElementHost form = new FormNoElementHost(commandData);

            form.ShowDialog();

            return Result.Succeeded;
        }
    }

 - Form (System.Windows.Forms.Form) :

 

public partial class FormNoElementHost : Forms.Form
    {
        ExternalCommandData _commandData;

        public FormNoElementHost(ExternalCommandData commandData)
            : this()
        {
            _commandData = commandData;
        }

        public FormNoElementHost()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Family family;
            string message;
            UtilsFamily.LoadFamily(_commandData.Application.ActiveUIDocument.Document, out family, out message);
        }
    }

 

WindowWPF: (lose focus)

 - Command :

    [Transaction(TransactionMode.Manual)]
    public class WindowWPFCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            WindowWPF  window = new WindowWPF(commandData);

            window.ShowDialog();

            return Result.Succeeded;
        }
    }

 - Window (System.Windows.Window) :

 

 

    public partial class WindowWPF : Window
    {
        ExternalCommandData _commandData;

        public WindowWPF(ExternalCommandData commandData)
            : this()
        {
            _commandData = commandData;
        }

        public WindowWPF()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Family family;
            string message;
            UtilsFamily.LoadFamily(_commandData.Application.ActiveUIDocument.Document, out family, out message);
        }
    }

 

 

 

 

 

UtilsFamily :

 

 

    public class UtilsFamily
    {
        public static bool LoadFamily(Document doc, out Family family, out string message)
        {
            family = null;
            message = null;

            // ask user to select a family file
            System.Windows.Forms.OpenFileDialog fileDlg = new System.Windows.Forms.OpenFileDialog();
            fileDlg.Filter = "Revit family (*.rfa)|*.rfa";
            fileDlg.Multiselect = false;
            if (fileDlg.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
                return false;

            using (Transaction tr = new Transaction(doc))
            {
                tr.Start("Load family");

                if (!doc.LoadFamily(fileDlg.FileName, new FamilyOption(), out family))
                {
                    message = "Could not load family !";
                    tr.RollBack();
                    return false;
                }

                tr.Commit();
            }
            return true;
        }
    }

 

 

 

 

 

Message 9 of 9
minet.axel
in reply to: minet.axel

Link of the video with the 3 examples: Link

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