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();
}
}
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.
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;
}
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;
}
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;
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
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;
}
}
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.