La aplicación WPF no se apaga al cerrar la ventana principal

82

Estoy acostumbrado a la programación de WinForms en Visual Studio, pero quería probar WPF.

Agregué otra ventana a mi proyecto, llamada Window01. La ventana principal se llama MainWindow. Antes del public MainWindow()constructor declaro Window01:

Window01 w1;

Ahora creo una instancia de esta ventana en:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    w1 = new Window01();            
}

Tengo un botón en el que se muestra la ventana: w1.ShowDialog();.

Lo 'gracioso' aquí es el hecho de que si inicio la aplicación (con depuración) y salgo unos segundos después (no hago nada en la aplicación), Visual Studio no deja de depurar como si la aplicación estuviera Aún corriendo.

Si muevo la línea w1 = new Window01();al método de clic de botón, es decir, justo arriba ShowDialog(), Visual Studio se está comportando correctamente, es decir, la depuración se detiene cuando salgo de la aplicación.

¿Por qué este extraño comportamiento?

gosr
fuente

Respuestas:

137

En su MainWindow.xaml.cs, intente hacer esto:

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);

    Application.Current.Shutdown();
}

Según este enlace, también puede configurar ShutdownModeen XAML:

http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode.aspx

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml"
    ShutdownMode="OnExplicitShutdown"
    >
</Application>

Las aplicaciones dejan de ejecutarse solo cuando se llama al Shutdownmétodo de Application. El cierre puede ocurrir implícita o explícitamente, según lo especificado por el valor de la ShutdownModepropiedad.

Si lo establece ShutdownModeen OnLastWindowClose, Windows Presentation Foundation (WPF) llama implícitamente a Shutdown cuando se cierra la última ventana de una aplicación, incluso si alguna de las ventanas instanciadas actualmente está configurada como la ventana principal (consulte MainWindow).

Una ShutdownModede OnMainWindowClosehace que WPF llame implícitamente a Shutdown cuando se cierra MainWindow, incluso si hay otras ventanas abiertas.

La vida útil de algunas aplicaciones puede no depender de cuándo se cierra la ventana principal o la última ventana, o puede que no dependa en absoluto de las ventanas. Para estos escenarios, debe establecer la ShutdownModepropiedad en OnExplicitShutdown, que requiere una Shutdownllamada de método explícita para detener la aplicación. De lo contrario, la aplicación continúa ejecutándose en segundo plano.

ShutdownMode se puede configurar de forma declarativa desde XAML o mediante programación desde el código.

Esta propiedad solo está disponible en el hilo que creó el Applicationobjeto.


En su caso, la aplicación no se cierra porque probablemente esté usando el valor predeterminado OnLastWindowClose:

Si lo establece ShutdownModeen OnLastWindowClose, WPF llama implícitamente a Shutdown cuando se cierra la última ventana de una aplicación, incluso si alguna de las ventanas instanciadas actualmente está configurada como la ventana principal (consulte MainWindow).

Dado que abre una nueva ventana y no la cierra, no se llama al cierre.

Bob Horn
fuente
1
Es importante tener en cuenta que la función de anulación es mejor si ha creado una instancia de otros subprocesos para realizar determinadas tareas. Detenga los subprocesos en la función OnClosed antes de cerrar hecho. De lo contrario, los subprocesos se mantienen vivos y la aplicación nunca sale realmente.
weezma2004
No estoy seguro, si es una buena idea cerrar una aplicación, solo porque MainWindow está cerrado. También es posible, ese proceso en segundo plano está tratando de hacer algo como guardar en DB, escribir en registros, almacenar cosas en caché, desconectar dispositivos, etc. Organice siempre el ciclo de vida de sus aplicaciones. Application.Current.Shutdown()cerrará la aplicación inmediatamente como si finalizara el proceso en la barra de tareas. Esto es como dispararle a alguien, antes de que diga su última palabra :) .. No lo recomendaría.
Vural
35

Me alegro de que haya recibido su respuesta, pero por el bien de los demás, también responderé su pregunta para agregar algo de información.


Paso 1

Primero, si desea que su programa se cierre cuando se cierre la ventana principal, debe especificarlo, ya que no es WinForms donde este comportamiento es predeterminado.

(El valor predeterminado en WPF es cuando se cierra la última ventana)

En codigo

Ir a la instancia de la aplicación en su punto de entrada (programa de WPF En VS 2012, el valor por defecto es el interior anidada App.xaml, así que vaya dentro de él y vaya a App.xaml.csUtilizar y crear un constructor).

En el constructor de especificar que el Application's ShutdownModedeben ser ShutdownMode. OnLastWindowClose.

    public App()
    {
        ShutdownMode = ShutdownMode.OnLastWindowClose;
    }

En XAML

Vaya a su App.xamlarchivo que VS 2012 crea de forma predeterminada (o crear usted mismo) La raíz es una Application, especifique el interior que sus Application's ShutdownModedeben ser ShutdownMode. OnLastWindowClose.

<Application x:Class="WpfApplication27.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         StartupUri="MainWindow.xaml"
         ShutdownMode="OnMainWindowClose">

Si funciona, ya está; puedes dejar de leer.


Paso 2

Si lo anterior no funcionó (supongo que escribió la aplicación WPF desde cero), la ventana principal probablemente no sea conocida por la aplicación como la ventana principal. Así que especifica eso también.

En codigo

Vaya al constructor de la aplicación como lo hizo en el Paso 1 y especifíquelo Application. MainWindowEl valor es tu Window:

MainWindow = mainWindow;

En XAML

Vaya al ApplicationXAML como lo hizo en el Paso 1 y especifíquelo Application. MainWindowEl valor es tu Window:

MainWindow = "mainWindow";

Alternativa

No creo que este es el mejor enfoque, sólo porque WPF no quiere que haga esto (por lo que tiene Application's ShutdownMode), pero sólo se puede utilizar un evento / sustituir un método evento (OnEventHappened).

Vaya al archivo de código subyacente de MainWindow y agregue:

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        App.Current.Shutdown();
    }
MasterMastic
fuente
3
Tu respuesta es mejor que la que está marcada.
Buscador
Por lo general, lo hago de esta manera: establecer MainWindowen la instancia de la ventana principal, establecer ShutdownModeen ShutdownMode.OnMainWindowClose, llamar MainWindow.Show()a la devolución de llamada al Startupevento de App. Esta es la solución más mínima que se me ocurrió y que funciona como se esperaba al menos en .NET Core 3 con WPF.
TorbenJ
11

Porque el modo de apagado predeterminado en una aplicación WPF es OnLastWindowClose, lo que significa que la aplicación se detiene cuando se cierra la última ventana.

Cuando crea una instancia de un nuevo objeto Window, se agrega automáticamente a la lista de ventanas en la aplicación. Entonces, el problema era que su aplicación estaba creando dos ventanas cuando se inició - MainWindow y Window01 aún no mostrada - y si solo cerraba MainWindow, Window01 mantendría su aplicación en ejecución.

Normalmente, creará un objeto de ventana en el mismo método que llamará a su ShowDialog, y creará un nuevo objeto de ventana cada vez que se muestre el diálogo.

Joe Daley
fuente
2

Me encontré con esta Pregunta cuando buscaba otra cosa y me sorprendió no poder ver ninguna de las respuestas propuestas que se mencionan Window.Owner.

    {
       var newWindow = new AdditionalWindow();
       newWindow.Owner = Window.GetWindow(this);

       // then later on show the window with Show() or ShowDialog()
    }

Una llamada a Window.GetWindow(this)es bastante útil en vista de una aplicación MVVM cuando se está lejos en el árbol visual sin saber dónde ha creado una instancia, se le puede llamar mediante el suministro de cualquier FrameWorkelemento (por ejemplo UserContol, Button, Page). Obviamente, si tiene una referencia directa a la ventana, use esa o incluso Application.Current.MainWindow.

Esta es una relación bastante poderosa que tiene una serie de beneficios útiles de los que es posible que no se dé cuenta al principio (suponiendo que no haya codificado específicamente ventanas separadas para evitar estas relaciones).

Si llamamos a su ventana principal MainWindowy la segunda ventana como AdditionalWindowentonces ...

  1. Minimizar MainWindowtambién minimizaráAdditionalWindow
  2. Restaurar MainWindowtambién restauraráAdditionalWindow
  3. El cierre MainWindowse cerrará AdditionalWindow, pero el cierre AdditionalWindowno se cerraráMainWindow
  4. AdditioanlWindownunca se "perderá" debajo MainWindow, es decir, AddditionalWindowsiempre se muestra arriba MainWindowen el orden z si solía Show()mostrarlo (¡bastante útil!)

Sin embargo, una cosa a tener en cuenta, si tiene esta relación, entonces no se llama al Closingevento en el AdditionalWindow, por lo que tendría que iterar manualmente sobre la OwnedWindowscolección. Por ejemplo, cree una forma de llamar a cada ventana a través de una nueva interfaz o método de clase base.

    private void MainWindow_OnClosing(object sender, CancelEventArgs e)
    {
        foreach (var window in OwnedWindows)
        {
            var win = window as ICanCancelClosing;  // a new interface you have to create
            e.Cancel |= win.DoYouWantToCancelClosing();
        }        
    }
Rhys
fuente
1

Nada de lo anterior funcionó para mí, tal vez porque nuestro proyecto usa Prism. Así que terminé usando esto en App.XAML.cs

    protected override void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);
        Process.GetCurrentProcess().Kill();
    }
sadis
fuente
0

Parece algo con lo que me encontré cuando creé una segunda ventana para actuar como un cuadro de diálogo. Cuando se abrió la segunda ventana y luego se cerró y luego se cerró la ventana principal, la aplicación siguió ejecutándose (en segundo plano). Agregué (o confirmé) lo siguiente en mi App.xaml:

<Application x:Class="XXXXXXX.App"
             ...  
             StartupUri="MainWindow.xaml"
             ShutdownMode="OnMainWindowClose">
    <Application.MainWindow >
        <NavigationWindow Source="MainWindow.xaml" Visibility="Visible" />
    </Application.MainWindow>

Sin alegría.

Entonces, finalmente entré en mi "MainWindow.xaml" y agregué una propiedad "Cerrada" a la ventana que fue a un método "MainWind_Closed" que se parece a lo siguiente:

 private void MainWind_Closed(object sender, EventArgs e)
        {
            foreach ( Window w in App.Current.Windows )
            {
                if (w.DataContext != this)
                    w.Close();
            }
        }

Al ejecutar el depurador, parece que la única ventana que aparece es la ventana que creé como diálogo; en otras palabras, el bucle foreach solo encuentra una ventana: el diálogo, no la principal.

Tenía "this.Close ()" ejecutándose en el método que cerraba el diálogo, y tenía un "dlgwin.Close ()" que venía después de "dlgwin.ShowDialog ()", y eso no funcionó. Ni siquiera un "dlgwin = null".

Entonces, ¿por qué no se cerraría ese diálogo sin estas cosas adicionales? Oh bien. Esto funciona.

Lowell
fuente