WPF: Невозможно повторно использовать окно после того, как оно было закрыто - PullRequest
43 голосов
/ 25 августа 2010

Я пытаюсь сохранить один экземпляр Window и, когда необходимо, вызвать ShowDialog. Это работает найти в winforms, но в WPF я получаю это исключение:

System.InvalidOperationException: Невозможно установить 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 ]

45 голосов
/ 25 августа 2010

Полагаю, вы могли бы сделать это, если бы вы изменили видимость окна, а не закрыли его.Вам нужно сделать это в событии Closing (), а затем отменить закрытие.Если вы разрешаете закрытие, вы, безусловно, не сможете открыть закрытое окно - с здесь :

Если событие закрытия не отменено, происходит следующее:

...

Неуправляемые ресурсы, созданные окном, удаляются.

После этого окно больше никогда не будет действительным.

НадеюсьХотя не стоит думать, что это стоит того, чтобы создавать новое окно каждый раз, когда приходится создавать новое окно, и это не так уж и сильно, а вероятность появления трудных для отладки ошибок / утечек памяти намного ниже.(Кроме того, вам нужно убедиться, что оно закрылось и освободило свои ресурсы, когда приложение закрыто)


Просто прочитайте, что вы используете ShowDialog (), это сделает окно модальным ипростое скрытие не вернет управление родительскому окну.Я сомневаюсь, что это возможно сделать с модальными окнами.

38 голосов
/ 19 августа 2011

Если я не ошибаюсь, вы можете отменить событие закрытия этого окна и вместо этого установить видимость скрытым

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        e.Cancel = true;
        this.Visibility = Visibility.Hidden;
    } 
2 голосов
/ 18 февраля 2014

Когда мы пытаемся показать закрытое окно, мы получим следующее исключение:

"Невозможно установить видимость или вызвать Show, ShowDialog или WindowInteropHelper.EnsureHandle после закрытия окна."

Так что для обработки этого случая было бы лучше, если бы мы использовали опцию Visibility окна.Нам нужно установить видимость окна на Скрытый или Свернутый вместо непосредственного его закрытия.

this.Visibility = System.Windows.Visibility.Свернут или скрыт;

Если мы хотим показать его снова, просто установите видимость на Видимый

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

2 голосов
/ 10 июня 2011
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;
    }
1 голос
/ 09 февраля 2011

если вы отмените событие закрытия и установите 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
0 голосов
/ 24 мая 2016

У меня была как-то похожая проблема. Таким образом, модальное диалоговое окно, но в этом диалоговом окне у вас есть кнопка «Выбрать», которая должна переключиться на основную форму (желательно без закрытия модального диалогового окна), выберите некоторую область оттуда и затем вернитесь обратно в модальное диалоговое окно с информацией выбора. Я попытался немного поиграть с немодальными диалогами / show / hide и после не смог найти ни одного хорошего (легко кодируемого) решения, закодировал как-то хакерский подход с использованием вызовов функций win32. То, что я проверил - хорошо работает с winforms, а также с xaml.

Сама проблема также не является необходимой простой - пользователь нажимает «Выбрать», и тогда он может забыть, что он что-то выбирал, и вернуться к тому же другому диалоговому окну выбора, что может привести к двум или более случаям. того же диалога.

Я пытаюсь решить эту проблему, используя статические переменные (экземпляр / родитель) - если у вас есть чистая winforms или чистая технология wpf, вы можете получить parent из 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

    ...

В той же парадигме могут возникнуть проблемы с немодальным диалогом, поскольку каждая цепочка вызовов функции выбора съедает стек, и в конечном итоге вы можете получить переполнение стека или проблемы с управлением состоянием родительского окна (включить / отключить его).

Так что я думаю, что это довольно легкое решение проблемы, даже если оно выглядит довольно сложным.

0 голосов
/ 23 апреля 2012

Вот как я справляюсь:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...