Windows Forms アプリケーションではメインウィンドウが閉じられた時にアプリケーションが終了していました。または ApplicationContext クラスを使用して終了条件を変更することもできました。WPF アプリケーションではどのような時にアプリケーションが終了するのでしょうか。

WPF アプリケーションの終了条件

 WPF では Application.Shutdown メソッドが呼び出されたときにアプリケーションが終了します。アプリケーションが終了する前に Exit イベントが発生します。Shutdown メソッドは明示的に呼び出さなくても暗黙的に呼び出す仕組みがあります。その動作を制御するのが Application.ShutdownMode プロパティです。

 ShutdownMode プロパティには次の三つの設定があります。

ShutdownMode プロパティ
  • OnLastWindowClose(既定値) アプリケーションは、最後のウィンドウが閉じるか、または Shutdown が呼び出されたときにシャットダウンします。
  • OnMainWindowClose アプリケーションは、メイン ウィンドウが閉じるか、または Shutdown が呼び出されたときにシャットダウンします。
  • OnExplicitShutdown アプリケーションは、Shutdown が呼び出されたときにのみシャットダウンします。

OnLastWindowClose

 ShutdownMode プロパティの既定値は OnLastWindowClose です。この設定の時は全ての Window インスタンスを閉じた時にアプリケーションが終了します。

 Application クラスではインスタンス化された Window を管理する Windows プロパティを持っています。Windows プロパティは Window のコレクションになっていて Window をインスタンス化すると自動で追加されます。Window を閉じるとコレクションから削除されます。そして全ての Window が Windows プロパティから削除された時に Shotdown メソッドが呼ばれます。

 この設定の時に注意することは Window は表示されていなくてもインスタンス化したら閉じないといけないということです。また、Windows プロパティのカウントが空になった時にアプリケーションが終了することを意識する必要があります。次のような時にはアプリケーションは終了しません(サンプルコードの Application_Startup メソッドは Application.Startup イベントをハンドルしています)。

アプリケーションが終了しないパターン1 ~ Window を一切表示しない時 ~
Private Sub Application_Startup(ByVal sender As System.Object, ByVal e As System.Windows.StartupEventArgs)
End Sub
' Windows プロパティのカウントが 0 から変化していないためアプリケーションは終了しません。
アプリケーションが終了しないパターン2 ~ Window をインスタンス化だけした時 ~
Private Sub Application_Startup(ByVal sender As System.Object, ByVal e As System.Windows.StartupEventArgs)
    Dim w As New Window
End Sub
' Windows プロパティのカウントが 1 になっているためアプリケーションは終了しません。
アプリケーションが終了しないパターン3 ~ Window のインスタンス化時に例外が発生した時 ~
Private Sub Application_Startup(ByVal sender As System.Object, ByVal e As System.Windows.StartupEventArgs)
    Dim w As New Window ' この時に例外が発生(異常終了しないようにしています)
    w.Close()
End Sub
' Windows プロパティのカウントが 1 になっているためアプリケーションは終了しません。

 パターン2のように Window をインスタンス化しただけでもアプリケーションは終了しなくなります。やっかいなのはパターン3のように Window のコンストラクタで例外が発生した場合です。Window のコンストラクタが呼ばれている時点で既に Windows プロパティに追加されています。上記の例では変数 w は Nothing のままです。そのため w.Close を実行することはできません。それでも Windows プロパティにはインスタンス化した Window が入っているので、それに対して Close メソッドを呼べれば Windows プロパティから削除することができます。しかし、どのようにしてコンストラクタで失敗した Window を探せばいいのでしょうか?

 WPF ではメイン UI スレッド以外にセカンダリスレッド上で Window を表示することができます。ただし、その Window は Windows プロパティに追加されません。セカンダリスレッド上の Window が表示されていても、メイン UI スレッド上の全ての Window が閉じられたときにアプリケーションは終了します。

 この他に気をつけることは、ログインウィンドウを表示してからメインウィンドウを表示したりする場合です。その時にログインウィンドウを閉じた時点でメインウィンドウがインスタンス化されていない場合は、アプリケーションが終了してしまいます。

ログインウィンドウを閉じるとアプリケーションが終了してしまう例
Private Sub Application_Startup(ByVal sender As System.Object, ByVal e As System.Windows.StartupEventArgs)
    Dim login As New LoginWindow
    If login.ShowDialog() = False Then
        Application.Current.Shutdown()
    End If
    login.Close() ' ここで Windows プロパティのカウントが 1 から 0 になるためアプリケーションが終了します。

    Dim main As New MainWindow
    main.Show()
End Sub

 ではどのようにしたらログインウィンドウからメインウィンドウを表示できるようになるのでしょうか? ShutdownMode プロパティには他に OnMainWindowClose と OnExplicitShutdown の設定があるため、そちらを使用すればいいかもしれません。長くなってきたので、その辺の内容は次回にしたいと思います。

まとめ

  • アプリケーションの終了条件は Application.ShutdownMode プロパティを使用する。
  • ShutdownMode が OnLastWindowClose に設定されているときは、メイン UI スレッドの全ての Window を閉じた時にアプリケーションが終了する。
  • Window はインスタンス化したら、表示しない場合でも必ず閉じるようにする。
  • 特に Window のコンストラクタから例外が発生しないようなコーディングをする。

 WPF アプリケーションの終了条件は ShutdownMode プロパティでいろいろ制御できるようです。ApplicationContext クラスがなくても終了条件を簡単に制御できるようになっているのはうれしいですね。しかし、OnLastWindowClose を設定した場合は、終了しなくなるパターンがあるなど注意することがあるので、OnLastWindowClose を設定するのは危険な感じがしました。