当在启动前显示对话框时,WPF应用程序会立即退出。
当在启动前显示对话框时,WPF应用程序会立即退出。
更新:我想,我需要了解在WPF中显示应用程序启动前的对话框的“正确”和“支持”的方法是什么。
以下是代码:
public partial class App : Application { [STAThread] public static void Main() { var app = new App(); app.InitializeComponent(); new DialogWindow().ShowDialog(); app.Run( new MainWindow() ); } }
DialogWindow
按预期显示。
但是在关闭它之后,应用程序立即退出。 MainWindow
根本没有显示!
我进行了一些调试,将问题追溯到这个问题:
- 当对话框被创建时,它成为
app
的MainWindow,因为此时没有MainWindow。 - 因此,关闭对话框会导致应用程序在调度程序队列中发布
ShutdownCallback
。 - 然而,调度程序没有运行足够长的时间来执行回调。
- 因此,一旦随后调用
app.Run
,队列中的第一件事就是ShutdownCallback
,这自然会导致应用程序立即关闭。
根据这个分析,有一个明显的解决方法:在App
之后立即创建MainWindow
,从而使其成为app
的MainWindow,这将防止DialogWindow
导致应用程序关闭。
然而,以下是让我困扰的地方。
首先,对我来说,这看起来像是一个不干净的技巧。我的意思是,没有明确的原因要按照这个顺序创建窗口,我只是通过一些调试找到了这个。这不可能是支持的方式。
其次,这显然是一个bug。我的意思是,如果在关闭之后创建第二个窗口不是明确支持的,那么应该抛出一些InvalidOperationException
,对吗?
第三,这不仅是一个bug,而且看起来是一个非常幼稚的bug,就像一个多线程初学者会创建的那样。
所有这些让我相信,也许我在这里没有理解到一些基本的东西?也许我根本就没有道理?也许应该用一种不同的方式来完成所有这些?
以下是一些背景信息:
应用程序在启动时必须进行一些引导。检查这个和那个,设置异常处理程序,日志记录-你知道,通常的事情。在这个过程中,可能需要向用户提出一些帮助-这就是对话框的用途。
我绝对不想将所有这些放在一种在MainWindow.IsVisibleChanged
上执行的状态机中。我希望保持它真正简单,简短和直接-引导代码应该是这样的,以便可以用肉眼发现错误。
问题的出现原因:
问题是因为在启动WPF应用程序时,在显示对话框之前应用程序立即退出。这可能是由于应用程序的ShutdownMode属性设置不正确导致的。
解决方法:
一种解决方法是将ShutdownMode属性设置为OnExplicitShutdown。这样可以避免将ShutdownCallback放入调度程序队列中,并且可以在不考虑窗口的情况下继续执行其他操作。另外,还可以使用Application.MainWindow属性来动态更改主窗口,这也是一个潜在的解决方法。
以下是使用C#代码演示如何解决这个问题:
using System; using System.Windows; namespace WpfApp { public partial class App : Application { public App() { // Set the ShutdownMode to OnExplicitShutdown ShutdownMode = ShutdownMode.OnExplicitShutdown; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // Show your dialog here var dialog = new MyDialog(); dialog.ShowDialog(); // Do whatever you want without regard to windows // ... // Call Shutdown when you are ready to exit the application Shutdown(); } } }
在这个示例中,我们在App类的构造函数中将ShutdownMode属性设置为OnExplicitShutdown。然后,在OnStartup方法中显示对话框,并在对话框关闭后继续执行其他操作。最后,通过调用Shutdown方法来退出应用程序。
除了使用ShutdownMode属性,还可以使用Application.MainWindow属性来动态更改主窗口。这可能是另一个解决这个问题的方法,但具体实现取决于应用程序的需求。
通过将ShutdownMode属性设置为OnExplicitShutdown,并在适当的时机调用Shutdown方法,可以避免在显示对话框之前应用程序立即退出的问题。此外,使用Application.MainWindow属性可以在运行时动态更改主窗口。这些方法可以根据具体的需求选择使用。
问题:WPF应用程序在启动前显示对话框时立即退出,出现这种行为的原因是什么?如何解决这个问题?
解决方法:
这个问题似乎是一个bug。通常情况下,我不会在Main()方法中放置任何内容,而是让不带参数的app.Run()方法被调用,并在OnStartup方法中调用所有需要的内容,但这并不会改变你所看到的行为。每当我需要在主应用程序窗口准备好显示之前显示一些内容,例如收集输入或显示一个启动画面,我都会在第二个线程上实现。
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // show splash var thread = new Thread(() => { Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashWindow().Show())); Dispatcher.Run(); } thread.SetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); // run configuration steps // instantiate and show main window }
显然,如果你在第二个线程上调用ShowDialog()方法,你需要确保在显示主窗口之前得到一个答案。这样做的好处是,在等待用户在特定部分输入时,您可以运行其他引导任务;这主要取决于各个任务的顺序。也许这对你的情况有所帮助,也可能没有 - 只是一个想法。
嗯,我已经有一个可行的解决方法了。我想知道应该如何“正常”地完成这个任务。不过,我怀疑对话框不应该从不同的线程中显示。话虽如此,这是一个好主意,谢谢。
我认为你应该使用一个显式的ShutdownMode:msdn.microsoft.com/en-us/library/…
WPF应用程序在显示启动窗口之前立即退出的问题是由于以下原因引起的:默认情况下,WPF应用程序的ShutdownMode属性设置为OnLastWindowClose。在代码中,您首先显示一个窗口,然后关闭它。因此,最后一个窗口被关闭,应用程序终止。然后,在关闭过程中,您显示了另一个窗口。由于应用程序正在关闭,该窗口立即关闭。
这一切都是按照您设定的设计和编程方式正常工作的。
然而,您想要做一些不同的事情:您首先显示的窗口应该是一个“特殊窗口”,在关闭它之后,您希望继续执行,显示您的“主窗口”,然后在它(或与应用程序关联的所有窗口)关闭后退出应用程序。
最简单的方法是:首先将ShutdownMode设置为OnExplicitShutdown,然后在显示主窗口后将其设置为OnLastWindowClose或OnMainWindowClose。代码如下:
public static void Main() { var app = new App(); app.InitializeComponent(); app.ShutdownMode = ShutdownMode.OnExplicitShutdown; new DialogWindow().ShowDialog(); var mainWindow = new MainWindow(); app.MainWindow = mainWindow; app.Run(mainWindow); // When the window has loaded, it should then set the app.ShutdownMode to what you actually want. }
编辑:
我不确定您具体在做什么。您提供的代码将无法编译,因为如果正确使用WPF应用程序类(具有App.xaml的Build Action设置为ApplicationDefinition),则已经定义了一个Main方法。如果您只有一个派生自Application的类,则没有InitializeComponent()方法。使代码能够编译的唯一方法是手动将Build Action更改为Page。然而,在这种情况下,Application.Current == app。
因此,发生的情况如下:
1. 应用程序启动。由于此时尚未创建WPF应用程序,Application.Current为null。这也意味着没有调度程序循环正在运行,调度程序消息未处理(请注意,调度程序循环还处理窗口消息)。
2. 创建一个新的App对象。由于Application.Current为null,它将自己设置为Application.Current。
- Application.Current.MainWindow为null,Application.Current.Windows为空列表。
- 由于ShutdownMode设置为OnLastWindowClose,一旦当前应用程序(即app)的最后一个窗口关闭,就会开始关闭。
3. 显示对话框窗口。由于没有调度程序循环正在运行,ShowDialog()本身运行一个“本地”调度程序循环。
- 实际上,有两个部分:首先创建窗口。它属于当前应用程序,因此将自己添加到Application.Current.Windows。由于它是第一个显示的窗口,并且Application.Current.MainWindow为null,它还将自己设置为主窗口。其次,以模态方式显示窗口。
- 由于Application.Current.Windows现在不为空,一旦它为空,就会开始关闭。
4. 用户关闭对话框窗口。作为关闭的一部分,窗口从Application.Current.Windows中删除自身。此外,由于它是MainWindow,所以将其设置为null。
- 由于Application.Current.Windows现在为空,开始关闭。然而,由于没有运行调度程序循环,因此什么也不会发生(只设置了一个内部标志或类似的事情)。
- 如果您使用了`app.Run(new DialogWindow()); app.Run(new MainWindow());`,则在创建MainWindow时会出现异常,因为在这种情况下,调度程序循环正常运行。因此,它实际上可以自行关闭,因此当创建MainWindow时,由于调度程序循环已关闭,它会抛出异常。
5. 创建MainWindow。如上所述,它将自己添加到Application.Current.Windows并将其设置为Application.Current.MainWindow。
- 但是,已经到达了关闭应用程序的条件。但是,到目前为止,应用程序还没有机会做些什么。
6. 现在调用Run()。调度程序循环重新开始,现在有机会关闭应用程序。因此,它关闭应用程序并关闭所有打开的窗口。
所以,再次强调,这不是一个bug。
因此,解决此问题的一种方法是将ShutdownMode更改为OnExplicitShutdown。然后,在第4步中,不会达到关闭的原因。更好的方法(更像是正常的WPF应用程序)是拥有一个正确的ApplicationDefinition。从App.xaml中删除StartupUri,并改为处理Startup事件:
private void OnStartup(object sender, StartupEventArgs e) { this.ShutdownMode = ShutdownMode.OnExplicitShutdown; new DialogWindow().ShowDialog(); var mainWindow = new MainWindow(); this.ShutdownMode = ShutdownMode.OnLastWindowClose; // or OnMainWindowClose mainWindow.Show(); }
由于在关闭对话框窗口时使用了OnExplicitShutdown,所以此时没有关闭应用程序的原因。然后,在创建MainWindow后,我们又有了一个窗口作为主窗口和应用程序窗口之一。因此,我们可以切换到实际所需的关闭模式并显示主窗口。
您的分析似乎是不正确的。我指的是第一个段落,您在其中说:“所以最后一个窗口关闭并且应用程序关闭。”实际上,这是不正确的:应用程序在那一刻并没有关闭。它只是启动了关闭的过程,但没有完成。只有当我调用Run()方法时,关闭过程才最终完成。事实上,如果关闭过程在我调用Run()之前完成,一切都会按照我期望的方式工作,因为WPF应用程序可以连续运行多次:启动、停止、再次启动、再次停止,依此类推。
但在这种情况下,应用程序启动了关闭过程,但没有完成,并且没有告诉我任何信息。因此,当我决定下一次启动它时,它只是完成了上一次遗留的关闭过程。在您看来,这似乎是个bug吗?
我的回答是:请仔细阅读我的编辑。这不是一个bug,而更像是对正在发生的事情的误解。
哦,请别这样,这开始变得令人讨厌了。是的,我知道它是如何工作的!实际上,在您的第一个评论中,您确切地说了您的编辑中所说的六个要点。然而,我们知道什么导致了一个bug,并不会让它不成为一个bug。
其次 - 是的,它确实可以编译。以下是提示:在创建项目后,您将得到一个带有嵌套的app.xaml文件和app.xaml.cs文件。打开那个*.cs文件,您将看到里面的Main()方法。我的代码就在那里。
不,它不能编译。如果在VS2008/VS2010下创建一个新的WPF应用程序(文件-新建-项目-WPF应用程序),app.xaml.cs文件包含一个空的类定义。如果您将您的代码复制到那里,将会得到一个错误,即Main()已经定义过了。其次,如果您仔细阅读六个要点,您会明白为什么关闭过程没有完成(使用ShowDialog()运行“本地调度程序循环”)。第三,我为您提供了两种解决方案。第四,不可能连续两次运行一个Application对象:一旦它关闭,您将会得到一个异常。