首页 文章

WPF:关闭后无法重用窗口

提问于
浏览
41

我试图保持一个 Window 的实例,并在需要时调用 ShowDialog . 这在winforms中找到了,但是在WPF中我接受了这个例外:

System.InvalidOperationException:无法在Window关闭后设置Visibility或调用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();
    }
}

7 回答

  • 0

    我想如果你改变窗口的可见性而不是关闭窗口,你可以这样做 . 你'd need to do that in the Closing() event and then cancel the close. If you allow the close to happen you certainly can' t重新打开一个关闭的窗口 - 来自here

    如果未取消Closing事件,则会发生以下情况:...窗口创建的非托管资源将被释放 .

    在那之后,窗口将再次无效 .

    我不认为这是值得的努力 - 每次创建一个新窗口并没有太大的性能影响,你不太可能引入难以调试的bug /内存泄漏 . (另外,当应用程序关闭时,您需要确保它关闭并释放它的资源)


    只是读到你正在使用ShowDialog(),这将使窗口模态并简单地隐藏它将不会将控制返回到父窗口 . 我怀疑用模态窗口可以做到这一点 .

  • 2

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

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = true;
            this.Visibility = Visibility.Hidden;
        }
    
  • 1
    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;
        }
    
  • 0

    当我们尝试显示关闭的Window时,我们将得到以下异常 .

    "Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed."

    因此,为了处理这种情况,如果我们使用窗口的 Visibility 选项会更好 . 我们需要将窗口的可见性设置为 HiddenCollapsed ,而不是直接关闭它 .

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

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

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

  • 43

    如果您取消关闭事件并设置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
    
  • 34

    我的处理方式如下:

    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;
        }
    }
    
  • 2

    我有某种类似的问题 . 所以模态对话框,但在该对话框中你有一个“选择”按钮,需要切换到主窗体(最好没有关闭模态对话框),从那里选择一些区域,然后返回到带有选择信息的模态对话框 . 我尝试使用无模式对话框/ show / hide进行一些操作,之后找不到任何好的(易于编码)解决方案,使用win32本机函数调用以某种方式编写hacky方法 . 我测试过 - 它与winforms和xaml一起使用效果很好 .

    问题本身也不是必须的 - 因此用户按下“选择”,然后他可能会忘记他正在选择某些内容,并返回到相同的不同选择对话框,这可能导致两个或更多相同对话框的实例 .

    我试图通过使用静态变量(实例/父)来解决这个问题 - 如果你有纯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 (e.g. 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
    
        ...
    

    相同的范例可能会出现无模式对话问题,因为每个选择函数调用链都会占用堆栈,最终可能会出现堆栈溢出,或者您可能会遇到管理父窗口状态(启用/禁用它)的问题 .

    所以我认为这是解决问题的非常轻量级的解决方案,因此它看起来相当复杂 .

相关问题