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?
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.
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
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
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 🙂
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; } }
Can't find what you're looking for? Ask the community or share your knowledge.