我有
public static int WindowCounter = 0; [STAThread] public static void Main() { ShowBeforeApplicationCreation(); //ShowAfterApplicationCreation(); } public static void ShowBeforeApplicationCreation() { ShowWindow(); ShowWindow(); ShowWindow(); var app = new Application(); app.Run(); } public static void ShowAfterApplicationCreation() { var app = new Application(); ShowWindow(); ShowWindow(); ShowWindow(); app.Run(); } public static void ShowWindow() { var window = new Window { Title = string.format("Title{0}", WindowCounter++) }; window.Show(); }
如果我如图所示运行代码,并closures任何窗口,它将closures所有窗口。 如果我切换到注释掉的部分,那么关于哪个窗口会导致所有窗口closures(大部分时间是最后一个)似乎是随机的。
应用程序如何决定哪个窗口会导致进程closures?
那个窗口是“主窗口”吗?
让我们定义一个closures进程的窗口,作为OwningWindow(希望这个名字在这个上下文中不会被重载)。 我怎样才能确保显示的第一个窗口是OwningWindow?
最后一个问题是我的主要问题。 我不想打开其他二级窗口,让用户closures主窗口,并让进程继续运行。 或者我必须使窗口的后续窗口的孩子,我想成为OwningWindow?
应用程序如何决定哪个窗口会导致进程关闭?
默认情况下, Application类将在所有打开的窗口关闭时退出Run()方法(从而退出整个过程)。 根据你的代码示例,似乎有一个皱纹: Application类不考虑它自己创建之前打开的任何窗口。 如果不告诉任何窗口(即通过传递Run()方法的引用),那么任何窗口关闭都会导致Run()方法退出,因为Application类确实看到了窗口的关闭,但认为在这个过程中没有窗户。
那个窗口是“主窗口”吗?
“主”窗口默认情况下是在AppDomain实例化的第一个窗口。 您可以随时通过设置Application对象的MainWindow属性来覆盖它。
当然,正如上面所讨论的, Application对象在创建之前没有办法看到创建的窗口。 例外:如果将窗口传递给Run()方法,那么即使窗口是在Application对象之前创建的,也可以成为MainWindow 。 如果所有窗口都是在Application之前创建的,并且您没有将引用传递给Run()方法,那么根本没有主窗口。
如果要确定性地分配一个窗口以在窗口关闭时导致进程退出,则一个选项是将其传递给Application.Run()方法。 例如:
class Program { public static int WindowCounter = 0; private static Window _firstWindow; [STAThread] public static void Main() { ShowBeforeApplicationCreation(); } public static void ShowBeforeApplicationCreation() { ShowWindow(); ShowWindow(); ShowWindow(); var app = new Application(); app.Run(_firstWindow); } public static void ShowWindow() { var window = new Window { Title = string.Format("Title{0}", WindowCounter++) }; window.Show(); _firstWindow = _firstWindow ?? window; } }
也就是说,默认的Application.ShutdownMode值是ShutdownMode.OnLastWindowClose ,所以不用做任何事情,程序不应该关闭,直到最后一个窗口关闭。 这只是因为类似乎没有注意到在实例化之前发生的窗口的开启(它显然是对窗口的实际创建作出反应,而不是在启动时搜索打开的窗口的过程)。
显然,在Application.Run()被调用之前创建窗口会混淆该方法; 似乎认为没有窗户仍然是开放的,如果任何关闭。 如果你传递给其中一个窗口的引用,它将不会返回到该窗口关闭之前,该窗口是它“知道”的唯一窗口; 任何其他窗口的关闭都被忽略,因为它所知道的一个窗口仍然是打开的。
(在我的测试中,程序不会退出,直到所有的窗口关闭,在Application对象之后创建它们的情况下,这与上面的想法是一致的,即Application类正确地记录了创建的窗口/或在创建之后显示)。
恕我直言,最好的办法是不要混淆的方法。 它不能正确处理在创建Application对象之前显示的窗口。 所以,不要这样做。 确保只有在创建Application后才创建所有的窗口。 如果您关闭的具体行为与默认的ShutdownMode.OnLastWindowClose行为不同,那么该行为应该足够简单,可以实现。
这可能就像将ShutdownMode属性设置为其他值一样简单。 例如, ShutdownMode.OnMainWindowClose 。 在这种情况下,您当然需要确保某个窗口是主窗口,否则该进程将不会退出。 您可以通过使用默认行为(即创建主窗口作为第一个窗口,但在创建Application对象之后),通过显式设置MainWindow属性,甚至通过将窗口引用传递给Run()方法在Application对象之前创建了所有的窗口。
让我们定义一个关闭进程的窗口,作为OwningWindow(希望这个名字在这个上下文中不会被重载)。 我怎样才能确保显示的第一个窗口是OwningWindow?
从上面的讨论中,你可以自己回答这个问题。 🙂
首先,“拥有窗口”实际上是MainWindow 。 但是,默认情况下, Application.Run()方法只有在所有的窗口(它知道)都关闭的时候才会返回。 只有通过隐藏Application窗口中的一些窗口才能看到某个“拥有窗口”负责关闭程序(在默认的ShutdownMode方案中)。
实际上,如果你想要的只是当某个特定窗口本身关闭时关闭程序,那么正确的方法是确保MainWindow设置正确,并且已经将Application.ShutdownMode设置为ShutdownMode.OnMainWindowClose值。 那样的话,WPF就完全明确了你想要的确切行为,即使你在创建Application对象之前做了一些奇怪的事情,也没有机会搞砸它。 🙂