WPF:closures后不能重新使用窗口

我试图保持一个Window周围的实例,并在需要时调用ShowDialog 。 这工作发现在winforms中,但在WPF我收到这种怀疑:

System.InvalidOperationException:无法设置可视性,或在窗口closures后调用Show,ShowDialog或WindowInteropHelper.EnsureHandle。

有没有办法在WPF中做这样的事情?

 MyWindow.Instance.ShowDialog(); public class MyWindow : Window { private static MyWindow _instance; public static MyWindow Instance { if( _instance == null ) { _instance = new Window(); } return _instance(); } } 

我想你可以做到这一点,如果你改变了窗口的可见性,而不是closures它。 您需要在Closing()事件中执行此操作,然后取消closures。 如果你允许接近发生,你当然不能重新打开一个closures的窗口 – 从这里 :

如果closures事件未被取消,则会发生以下情况:

处理由Window创build的非托pipe资源。

发生这种情况后,窗口永远不会再有效。

我不认为这是值得的 – 尽pipe每次创build一个新窗口的性能并没有太大的提高,而且你也不太可能很难debuggingbug /内存泄漏。 (另外,当你closures应用程序时,你需要确保它closures并释放资源)


只要阅读您正在使用ShowDialog(),这将使窗口模式,只是隐藏它不会返回到父窗口的控制。 我怀疑这是完全可能的模式窗口。

如果我没有错,可以取消该窗口的closures事件,而是将可见性设置为隐藏

 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; this.Visibility = Visibility.Hidden; } 
 public class MyWindow : Window public MyWindow () { InitializeComponent(); Closed += new System.EventHandler(MyWindow_Closed); } private static MyWindow _instance; public static MyWindow Instance { if( _instance == null ) { _instance = new Window(); } return _instance(); } void MyWindow_Closed(object sender, System.EventArgs e) { _instance = null; } 

当我们试图显示被closures的窗口时,我们会得到以下例外。

“窗口closures后,无法设置可视性或调用Show,ShowDialog或WindowInteropHelper.EnsureHandle”。

所以为了处理这种情况,如果我们使用窗口的可见性选项会更好。 我们需要将窗口的可见性设置为隐藏折叠,而不是直接closures窗口。

this.Visibility = System.Windows.Visibility.Collapsed或Hidden;

如果我们想再次显示,只需将可见性设置为可见即可

this.Visibility = System.Windows.Visibility.Visible;

如果取消closures事件并设置visibility = hidden,则可以覆盖此问题

 Private Sub ChildWindow_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing e.Cancel = True Me.Visibility = Windows.Visibility.Hidden End Sub 

这是我如何处理:

 public partial class MainWindow { bool IsAboutWindowOpen = false; private void Help_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (!IsAboutWindowOpen) { var aboutWindow = new About(); aboutWindow.Closed += new EventHandler(aboutWindow_Closed); aboutWindow.Show(); IsAboutWindowOpen = true; } } void aboutWindow_Closed(object sender, EventArgs e) { IsAboutWindowOpen = false; } } 

我有类似的问题。 所以modal dialog,但在该对话框中你有button“select”,需要切换到主窗体(最好不closures模式对话框),从那里select一些区域,然后返回到select信息的模式对话框。 我试图用无模式对话框/显示/隐藏和后找不到任何好的(易于编码)的解决scheme,使用win32本机函数调用编码某种方式hacky。 我所testing的 – 它与winforms和xaml的效果很好。

问题本身也不是一件容易的事情 – 所以用户按下“select”,然后他可能会忘记他正在select一些东西,并返回到同一个不同的select对话框,这可能会导致两个或多个同一个对话框的实例。

我试图通过使用静态variables(实例/父母)来解决这个问题 – 如果你有纯粹的winforms或纯wpf技术,你可能会从instance.Parent或instance.Owner得到父。

 public partial class MeasureModalDialog : Window { // Dialog has "Select area" button, need special launch mechanism. (showDialog / SwitchParentChildWindows) public static MeasureModalDialog instance = null; public static object parent = null; static public void showDialog(object _parent) { parent = _parent; if (instance == null) { instance = new MeasureModalDialog(); // Parent is winforms, child is xaml, this is just glue to get correct window owner to child dialog. if (parent != null && parent is System.Windows.Forms.IWin32Window) new System.Windows.Interop.WindowInteropHelper(instance).Owner = (parent as System.Windows.Forms.IWin32Window).Handle; // Enable parent window if it was disabled. instance.Closed += (_sender, _e) => { instance.SwitchParentChildWindows(true); }; instance.ShowDialog(); instance = null; parent = null; } else { // Try to switch to child dialog. instance.SwitchParentChildWindows(false); } } //showDialog public void SwitchParentChildWindows( bool bParentActive ) { View3d.SwitchParentChildWindows(bParentActive, parent, this); } public void AreaSelected( String selectedAreaInfo ) { if( selectedAreaInfo != null ) // Not cancelled textAreaInfo.Text = selectedAreaInfo; SwitchParentChildWindows(false); } private void buttonAreaSelect_Click(object sender, RoutedEventArgs e) { SwitchParentChildWindows(true); View3d.SelectArea(AreaSelected); } ... public static class View3d { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool EnableWindow(IntPtr hWnd, bool bEnable); [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] static extern bool BringWindowToTop(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsWindowEnabled(IntPtr hWnd); /// <summary> /// Extracts window handle in technology independent wise. /// </summary> /// <param name="formOrWindow">form or window</param> /// <returns>window handle</returns> static public IntPtr getHandle( object formOrWindow ) { System.Windows.Window window = formOrWindow as System.Windows.Window; if( window != null ) return new System.Windows.Interop.WindowInteropHelper(window).Handle; System.Windows.Forms.IWin32Window form = formOrWindow as System.Windows.Forms.IWin32Window; if (form != null) return form.Handle; return IntPtr.Zero; } /// <summary> /// Switches between modal sub dialog and parent form, when sub dialog does not needs to be destroyed (eg selecting /// something from parent form) /// </summary> /// <param name="bParentActive">true to set parent form active, false - child dialog.</param> /// <param name="parent">parent form or window</param> /// <param name="dlg">sub dialog form or window</param> static public void SwitchParentChildWindows(bool bParentActive, object parent, object dlg) { if( parent == null || dlg == null ) return; IntPtr hParent = getHandle(parent); IntPtr hDlg = getHandle(dlg); if( !bParentActive ) { // // Prevent recursive loops which can be triggered from UI. (Main form => sub dialog => select (sub dialog hidden) => sub dialog in again. // We try to end measuring here - if parent window becomes inactive - // means that we returned to dialog from where we launched measuring. Meaning nothing special needs to be done. // bool bEnabled = IsWindowEnabled(hParent); View3d.EndMeasuring(true); // Potentially can trigger SwitchParentChildWindows(false,...) call. bool bEnabled2 = IsWindowEnabled(hParent); if( bEnabled != bEnabled2 ) return; } if( bParentActive ) { EnableWindow(hDlg, false); // Disable so won't eat parent keyboard presses. ShowWindow(hDlg, 0); //SW_HIDE } EnableWindow(hParent, bParentActive); if( bParentActive ) { SetForegroundWindow(hParent); BringWindowToTop(hParent); } else { ShowWindow(hDlg, 5 ); //SW_SHOW EnableWindow(hDlg, true); SetForegroundWindow(hDlg); } } //SwitchParentChildWindows ... 

由于每个select函数的调用链吃掉堆栈,最终可能会导致堆栈溢出,或者在pipe理父窗口状态(启用/禁用它)方面也会遇到问题,所以同样的范例可能会出现无模式对话的问题。

所以我认为这是一个相当轻量级的解决scheme,即使这样看起来相当复杂。