Steve
Steve

Reputation: 11963

Why would Window.Close event propagate?

I ran into a weird case where Close event of the child window propagate to the parent window and causes it to close as well.

I made a minimum example as shown below

For TestWindow there is nothing but the default WPF window generated by VS

and in App.xaml.cs I override the OnStartup event and use it as a custom Main function

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    TestWindow t = new TestWindow();
    t.ShowDialog();
}

Now if you click on the X button to close TestWindow, the application shuts down instead of showing the MainWindow. If you comment out t.ShowDialog then the MainWindow will show just fine. Next if you listen to the Closing event of MainWindow you will find that it will trigger after the TestWindow closes which doesn't seem right to me

Upvotes: 4

Views: 644

Answers (2)

user585968
user585968

Reputation:

It's not actually propagating, WPF runs your first dialog and upon closing notices that the process has no further windows present. WPF posts an application quit message for later processing. In the meantime your code has proceeded to display a further window which when processing the message pump encounters the quit message and so closes the window and terminates your app.

Debug log:

Information: 0 : App OnStartup

Information: 0 : new MainWindow

Information: 0 : MainWindow closing

Information: 0 : App exiting

To resolve, you need to remove the StartupUri and instead handle the Startup event.

Change:

<Application x:Class="WpfCloseProblem.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:WpfCloseProblem"
         StartupUri="MainWindow.xaml"> ...

...to:

<Application x:Class="WpfCloseProblem.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfCloseProblem"
             Startup="Application_Startup">

Then discard the code on OnStartup and instead define a handler for Startup:

//protected override void OnStartup(StartupEventArgs e)
//{
//    base.OnStartup(e);
//
//    TestWindow t = new TestWindow();
//    t.ShowDialog();
//}

private void Application_Startup(object sender, StartupEventArgs e)
{
    var main = new MainWindow();

    TestWindow t = new TestWindow();
    t.ShowDialog();

    main.Show();
}

Previously I was able to confirm that after the dialog closed, MainWindow was created; loaded and closed in quick succession.

Upvotes: 4

King King
King King

Reputation: 63317

The way App works here is it chooses the first started window as the main window. So in your case, the TestWindow will be chosen as the main window. The ShutdownMode in your code is somehow set to OnMainWindowClose. So after closing TestWindow, all child windows (including your MainWindow) have their Closing fired.

So the problem here is not propagating up, but propagating down the closing event.

You should not create any window before your main window actually being started first. Or if you want, you can set ShutdownMode to OnLastWindowClose.

protected override void OnStartup(StartupEventArgs e)
{
  base.OnStartup(e);
  Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
  TestWindow t = new TestWindow();
  t.ShowDialog();
}

Or you can set the MainWindow explicitly in the constructor of your main window:

public MainWindow(){ 
  InitializeComponent();
  Application.Current.MainWindow = this;
}

However if using ShowDialog(), there is no way for you to set the MainWindow explicitly. Because right after closing the TestWindow (at that time it's still main window), the whole app will be shutdown.

Edit: I don't find any reference about this but it can be checked and we can be sure about that, here is the debugging:

protected override void OnStartup(StartupEventArgs e)
{
  base.OnStartup(e);
  new TestWindow();//not even need to be shown
  var wm = Application.Current.MainWindow;// points to the new TestWindow
  //If there is not any Window init here, the MainWindow is just null      
}

Upvotes: 3

Related Questions