WPF: fenêtre non réutilisable après fermeture

je suis en train de garder une instance de Window autour et en cas de besoin appeler ShowDialog. Cela a fonctionné trouver dans winforms, mais dans WPF je recieve cette exeception:

Système.InvalidOperationException: impossible de définir la visibilité ou le spectacle d'appel, ShowDialog ou WindowInteropHelper.EnsureHandle après la fermeture d'une fenêtre.

y a-t-il un moyen de faire quelque chose comme ça dans WPF?

MyWindow.Instance.ShowDialog();

public class MyWindow : Window
{
    private static MyWindow _instance;

    public static MyWindow Instance
    {
        if( _instance == null )
        {
            _instance = new Window();
        }
        return _instance();
    }
}
41
demandé sur Jerod Houghtelling 2010-08-25 20:37:43

7 réponses

je suppose que vous faites-le si vous avez changé la visibilité de la fenêtre au lieu de la fermer. Vous devez faire cela dans L'événement de fermeture() et ensuite annuler la fermeture. Si vous permettez à la fermeture de se produire, vous ne pouvez certainement pas rouvrir une fenêtre fermée-de ici:

Si l'événement de Clôture n'est pas annulé, le suivant se produit:

...

les ressources non gérées créées par la fenêtre sont détruits.

après cela, la fenêtre ne sera plus jamais valide.

Je ne pense pas que cela en vaille la peine, cependant - ce n'est vraiment pas un grand succès de performance pour faire une nouvelle fenêtre à chaque fois et vous êtes beaucoup moins susceptibles d'introduire des bugs difficiles à déboguer / fuites de mémoire. (De plus, vous devez vous assurer qu'il a fait fermer et libérer ses ressources lorsque l'application est fermée)


il suffit de lire que vous utilisez ShowDialog(), cela rendra la fenêtre modale et le cacher ne ramènera pas le contrôle à la fenêtre mère. Je doute qu'il soit possible de le faire avec des fenêtres modales.

43
répondu Martin Harris 2010-08-25 16:45:54

si Je ne me trompe pas, vous pouvez annuler l'événement de fermeture de cette fenêtre et à la place mettre la visibilité à hidden

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        e.Cancel = true;
        this.Visibility = Visibility.Hidden;
    } 
33
répondu Rain 2012-12-25 06:33:17
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;
    }
2
répondu Winson Yang 2011-06-10 07:36:36

Quand nous essayons de montrer la Fenêtre est fermée, nous aurons l'exception suivante.

" impossible de définir la visibilité ou le spectacle d'appel, le ShowDialog ou WindowInteropHelper.EnsureHandle après la fermeture d'une fenêtre."

alors pour gérer cette affaire il serait préférable d'utiliser Visibilité option de la fenêtre. Nous devons définir la visibilité de la fenêtre à caché ou S'est effondré au lieu de le fermer directement.

ceci.Visibilité = Système.Windows.Visibilité.S'est effondré ou Cachés;

Si nous voulons montrer à nouveau, il suffit de régler la visibilité Visible

ceci.Visibilité = Système.Windows.Visibilité.Visible;

2
répondu Srikanth Dornala 2014-02-18 09:27:44

si vous annulez l'événement de fermeture et définissez visibility =hidden alors vous pouvez outrepasser ce problème

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
1
répondu Shiyas 2013-02-25 16:15:38

Voici comment je gère :

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;
    }
}
0
répondu Rumplin 2012-04-23 10:30:00

j'avais quelque problème similaire. Donc Dialogue modal, mais dans ce dialogue vous avez bouton "Select" qui doit passer à la forme principale (de préférence sans fermer Dialogue modal), sélectionnez une zone de là et puis revenir à Dialogue modal avec des informations de sélection. J'ai essayé de jouer un peu avec modeless dialogs / show / hide et après n'a pas pu trouver de bonne (facile à coder) solution, codé en quelque sorte approche hacky en utilisant des appels de fonction natifs win32. Ce que j'ai testé, il fonctionne bon avec winforms et aussi avec xaml.

le problème lui - même n'est pas non plus nécessaire facile Un-donc l'utilisateur appuie sur "Select", et puis il pourrait oublier qu'il sélectionnait quelque chose, et revenir à la même boîte de dialogue de sélection différente, ce qui peut conduire à deux ou plusieurs instances de même boîte de dialogue.

j'essaie de résoudre ce problème en utilisant des variables statiques (instance / parent) - si vous avez des winforms purs ou une technologie WPF purs, vous pourriez obtenir parent de instance.Parent ou de l'instance.Propriétaire.

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

    ...

même paradigme pourrait avoir des problèmes dialogue modeless, puisque chaque chaîne d'appel de fonction de sélection mange pile et éventuellement vous pourriez obtenir le débordement de pile, ou vous pourriez avoir des problèmes aussi avec la gestion de l'état de fenêtre parent (activer / désactiver it).

donc je pense que c'est une solution assez légère à un problème, même si cela semble plutôt complexe.

0
répondu TarmoPikaro 2016-05-24 15:05:47