关闭主窗口时 WPF 应用程序不会关闭

我已经习惯了在 VisualStudio 中使用 WinForms 编程,但是我想尝试一下 WPF。

我在我的项目中添加了另一个窗口,叫做 Windows01。主窗口称为 MainWindow。在 public MainWindow()构造函数之前,我声明 Windows01:

Window01 w1;

现在我实例化这个窗口:

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

我有一个按钮的窗口显示: w1.ShowDialog();

这里“有趣”的事实是,如果我启动应用程序(使用调试)并在几秒钟后退出(我不在应用程序中做任何事情) ,Visual Studio 不会停止调试,就好像应用程序仍然在运行一样。

如果我将代码行 w1 = new Window01();移动到按钮单击方法,这意味着刚好在 ShowDialog()之上,Visual Studio 的工作正常——也就是说,当我退出应用程序时调试停止。

为什么会有这种奇怪的行为?

81938 次浏览

In your MainWindow.xaml.cs, try doing this:

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


Application.Current.Shutdown();
}

Per this link, you can also set the ShutdownMode in 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>

Applications stop running only when the Shutdown method of the Application is called. Shut down can occur implicitly or explicitly, as specified by the value of the ShutdownMode property.

If you set ShutdownMode to OnLastWindowClose, Windows Presentation Foundation (WPF) implicitly calls Shutdown when the last window in an application closes, even if any currently instantiated windows are set as the main window (see MainWindow).

A ShutdownMode of OnMainWindowClose causes WPF to implicitly call Shutdown when the MainWindow closes, even if other windows are currently open.

The lifetime of some applications may not be dependent on when the main window or last window is closed, or may not be dependent on windows at all. For these scenarios you need to set the ShutdownMode property to OnExplicitShutdown, which requires an explicit Shutdown method call to stop the application. Otherwise, the application continues running in the background.

ShutdownMode can be configured declaratively from XAML or programmatically from code.

This property is available only from the thread that created the Application object.


In your case, the app isn't closing because you're probably using the default OnLastWindowClose:

If you set ShutdownMode to OnLastWindowClose, WPF implicitly calls Shutdown when the last window in an application closes, even if any currently instantiated windows are set as the main window (see MainWindow).

Since you're opening a new window, and not closing it, shutdown doesn't get called.

Because the default shutdown mode in a WPF application is OnLastWindowClose, which means the application stops when the last window closes.

When you instantiate a new Window object, it automatically gets added to the list of windows in the application. So, the problem was that your application was creating two windows when it started - the MainWindow and the not-yet-shown Window01 - and if you only closed the MainWindow, the Window01 would keep your application running.

Normally, you will create a window object in the same method that is going to call its ShowDialog, and you will create a new window object each time the dialog is shown.

I'm glad you got your answer but for the sake of others I'll answer your question as-well to add some information.


Step 1

First, if you want your program to exit when the main window closes down, you need to specify, since this is not WinForms where this behavior is default.

(The default in WPF is when the last window closes down)

In Code

Go to your application instance in your entry point (In VS 2012's WPF program the default is nested inside App.xaml, so go inside it and navigate to App.xaml.cs & create a constructor).

In the constructor specify that your Application's ShutdownMode should be ShutdownMode.OnLastWindowClose.

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

In XAML

Go to your App.xaml file that VS 2012 created by default (or create it yourself) The root is an Application, specify inside that your Application's ShutdownMode should be 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">

If it works, you're done; you can stop reading.


Step 2

If the above didn't work (I guess you wrote the WPF application from scratch), the main window probably isn't known to the application as the main window. So specify that as-well.

In Code

Go to the application's constructor as you did in Step 1, and specify that Application.MainWindow's value is your Window:

MainWindow = mainWindow;

In XAML

Go to the Application XAML as you did in Step 1, and specify that Application.MainWindow's value is your Window:

MainWindow = "mainWindow";

Alternative

I don't think this is the best approach, just because WPF doesn't want you to do this (so it has Application's ShutdownMode), but you can just use an event / override an event method (OnEventHappened).

Go to the MainWindow's code-behind file and add:

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


App.Current.Shutdown();
}

It looks like something I ran into when I created a second window to act as a dialog box. When the second window was opened and then closed and then the main window was closed, the app kept running (in the background). I added (or confirmed) the following in my App.xaml:

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

No joy.

So, I finally went into my "MainWindow.xaml" and added a "Closed" property to the Window which went to a "MainWind_Closed" method that looks like the following:

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

Running through the debugger, it looks like the only window that shows up is the window I created as a dialog--in other words, the foreach loop only finds one window--the dialog, not the main.

I had "this.Close()" running in the method that closed the dialog, and I had a "dlgwin.Close()" that came after the "dlgwin.ShowDialog()", and that didn't work. Not even a "dlgwin = null".

So, why wouldn't that dialog close without this extra stuff? Oh well. This works.

I stumbled across this Question when searching for something else and I was surprised that I could not see any of the proposed answers mentioning about Window.Owner.

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


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

A call to Window.GetWindow(this) is quite useful in View of an MVVM application when you are far down the visual tree not knowing where you have been instantiated, it can be called by supplying any FrameWork element (e.g. UserContol, Button, Page). Obviously if you have a direct reference to the window then use that or even Application.Current.MainWindow.

This is quite a powerful relationship that has a number of useful benefits that you might not realise to begin with (assuming you have not specifically coded separate windows to avoided these relationships).

If we call your main window MainWindow and the second window as AdditionalWindow then....

  1. Minimising the MainWindow will also minimise AdditionalWindow
  2. Restoring MainWindow will also restore AdditionalWindow
  3. Closing MainWindow will close AdditionalWindow, but closing AdditionalWindow will not close MainWindow
  4. AdditioanlWindow will never get "lost" under MainWindow, i.e. AddditionalWindow always shows above MainWindow in the z-order if you used Show() to display it (quite useful!)

One thing to note though, if you have this relationship, then the Closing event on the AdditionalWindow is not called, so you'd have to manually iterate over the OwnedWindows collection. e.g. Create a way to call each window via a new Interface, or base class method.

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

None of the above worked for me, maybe because our project uses Prism. So ended up using this in the App.XAML.cs

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