WPF应用程序关闭主窗口时未关闭

93

我习惯于在Visual Studio中使用WinForms进行编程,但我想尝试一下WPF。

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

Window01 w1;

现在我将在此处实例化这个窗口:

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

我有一个按钮,当按下它时会弹出一个窗口:w1.ShowDialog();

有趣的是,如果我启动应用程序(调试模式),然后几秒钟后退出它(在应用程序中什么也不做),Visual Studio没有停止调试,好像应用程序仍在运行一样。

如果我将w1 = new Window01();这一行移到按钮单击方法中,也就是在ShowDialog()的正上方,那么Visual Studio就会表现正常 - 也就是说,当我退出应用程序时,调试会停止。

为什么会出现这种奇怪的行为呢?

6个回答

158

在你的MainWindow.xaml.cs文件中,尝试这样做:

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

    Application.Current.Shutdown();
}

根据此链接,您也可以在XAML中设置ShutdownMode

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>
应用程序仅在调用ApplicationShutdown方法时停止运行。关闭可以隐式或显式发生,具体取决于ShutdownMode属性的值。
如果将ShutdownMode设置为OnLastWindowClose,则Windows Presentation Foundation (WPF)在应用程序中的最后一个窗口关闭时会隐式地调用Shutdown,即使任何当前实例化的窗口都被设置为主窗口(请参见MainWindow)。 OnMainWindowCloseShutdownMode会导致WPF在MainWindow关闭时隐式调用Shutdown,即使其他窗口当前已打开。
某些应用程序的生命周期可能不依赖于主窗口或最后一个窗口何时关闭,或者根本不依赖于窗口。对于这些情况,您需要将ShutdownMode属性设置为OnExplicitShutdown,这需要显式调用Shutdown方法来停止应用程序。否则,应用程序将在后台继续运行。 ShutdownMode可以从XAML声明式配置或从代码编程性地配置。
此属性仅从创建Application对象的线程中可用。
在您的情况下,应用程序没有关闭,因为您可能正在使用默认的OnLastWindowClose
如果将ShutdownMode设置为OnLastWindowClose,则WPF在应用程序中的最后一个窗口关闭时会隐式地调用Shutdown,即使任何当前实例化的窗口都被设置为主窗口(请参见MainWindow)。
由于您正在打开一个新窗口,并且未关闭它,因此不会调用Shutdown。

1
需要注意的是,如果您已经实例化了其他线程来执行某些任务,则覆盖函数是最好的选择。在关闭函数OnClosed之前停止这些线程,然后再关闭。否则,这些线程将保持活动状态,应用程序永远无法真正退出。 - weezma2004
我不确定仅因为MainWindow关闭就关闭应用程序是否是一个好主意。也有可能后台进程正在尝试做一些事情,比如保存到数据库、写入日志、缓存等等...此时,我会找出阻止应用程序关闭的问题并加以修复。始终组织好你的应用程序生命周期。Application.Current.Shutdown()将立即关闭应用程序,就像在任务栏上结束进程一样。这就像在他说最后一句话之前就开枪打死了他 :)..我不建议这样做。 - Vural

41

很高兴你找到了答案,但为了其他人着想,我也会回答你的问题并添加一些信息。


步骤1

首先,如果您希望程序在主窗口关闭时退出,则需要指定此行为,因为这不是 WinForms 的默认行为(WPF 中的默认设置是当最后一个窗口关闭时)。

(WPF 中的默认设置是当最后一个窗口关闭时)

在代码中

进入您的应用程序实例所在的入口点(在 VS 2012 的 WPF 程序中,默认嵌套在 App.xaml 中,因此请进入其中并导航至 App.xaml.cs 并创建构造函数)。

在构造函数中指定您的 ApplicationShutdownMode 应为 ShutdownMode.OnLastWindowClose

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

在 XAML 中

打开默认情况下 VS 2012 创建的 App.xaml 文件(或自己创建), 根节点是一个 Application,在里面指定你的 ApplicationShutdownMode 应该是 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">
如果它起作用了,那么你就完成了;可以停止阅读。

步骤2

如果上面的方法不起作用(我猜你是从头开始编写WPF应用程序),主窗口可能不为应用程序所知道。 因此也要指定它。

在代码中

像第1步一样,进入应用程序的构造函数,并指定 Application.MainWindow 的值是你的Window:

MainWindow = mainWindow;

XAML中的设置:

在第一步中所做的方式,转到Application XAML,并指定 Application.MainWindow 的值为您的Window

MainWindow = "mainWindow";

替代方案

我认为这不是最佳的方法,只是因为WPF并不希望你这样做(所以它有 ApplicationShutdownMode),但你可以使用一个事件/覆盖一个事件方法(OnEventHappened)。

进入 MainWindow 的代码后台文件并添加:

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

        App.Current.Shutdown();
    }

4
你的回答比被标记的那个更好。 - Seekeer
我通常是这样做的:将MainWindow设置为主窗口的实例,将ShutdownMode设置为ShutdownMode.OnMainWindowClose,在AppStartup事件回调中调用MainWindow.Show()。这是我想到的最简单的解决方案,至少在.NET Core 3和WPF中可以正常工作。 - TorbenJ

11
因为WPF应用程序默认的关闭模式是OnLastWindowClose,这意味着当最后一个窗口关闭时应用程序将停止。
当您实例化一个新的Window对象时,它会自动添加到应用程序的窗口列表中。所以,问题在于当应用程序启动时,它会创建两个窗口 - MainWindow和尚未显示的Window01 - 如果您只关闭了MainWindow,Window01将使您的应用程序继续运行。
通常,您会在调用ShowDialog的同一方法中创建一个窗口对象,并且每次显示对话框时都会创建一个新的窗口对象。

3

我在搜索其他内容时偶然发现了这个问题,惊讶的是我没有看到任何提出的答案提到Window.Owner

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

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

在MVVM应用程序中,当您在视觉树下方且不知道已实例化的位置时,调用Window.GetWindow(this)非常有用。它可通过提供任何Framework元素(例如UserContolButtonPage)来调用。显然,如果您拥有窗口的直接引用,则使用该引用或甚至使用Application.Current.MainWindow
这是一种非常强大的关系,具有许多有用的好处,您可能开始时没有意识到(假设您没有专门编写单独的窗口以避免这些关系)。
如果我们将主窗口称为MainWindow,第二个窗口称为AdditionalWindow,那么....
  1. 最小化MainWindow也会最小化AdditionalWindow
  2. 还原MainWindow也会还原AdditionalWindow
  3. 关闭MainWindow将关闭AdditionalWindow,但关闭AdditionalWindow不会关闭MainWindow
  4. 如果您使用Show()显示AdditioanlWindow,则AdditioanlWindow永远不会在MainWindow下“丢失”,即AdditionWindow始终在z顺序中显示在MainWindow上方(非常有用!)
需要注意的一件事是,如果您具有此关系,则不会调用AdditionalWindow上的Closing事件,因此您必须手动迭代OwnedWindows集合。例如:创建一种通过新接口或基类方法调用每个窗口的方法。
    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();
        }        
    }

3

以上的方法都没有对我起作用,可能是因为我们的项目使用了Prism。所以最终我在 App.XAML.cs 中使用了以下代码:

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

1

看起来像是我创建第二个窗口作为对话框时遇到的问题。当第二个窗口被打开、关闭,然后主窗口被关闭时,应用程序仍在后台运行。我在我的App.xaml中添加(或确认)了以下内容:

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

没有喜悦。
所以,我最终进入了我的“MainWindow.xaml”并为窗口添加了一个“Closed”属性,该属性指向一个名为“MainWind_Closed”的方法,其代码如下:
 private void MainWind_Closed(object sender, EventArgs e)
        {
            foreach ( Window w in App.Current.Windows )
            {
                if (w.DataContext != this)
                    w.Close();
            }
        }

通过调试器运行,看起来唯一显示的窗口是我创建为对话框的窗口——换句话说,foreach循环只找到一个窗口——对话框,而不是主窗口。

我在关闭对话框的方法中运行了"this.Close()",并且在"dlgwin.ShowDialog()"之后有一个"dlgwin.Close()",但这并没有起作用。甚至"dlgwin = null"也没用。

那么,为什么没有这些额外的东西对话框就无法关闭呢?哦,好吧。这个方法可行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接