アプリケーションのメインフォームを表示する時、エントリポイント(詳しくは、「アプリケーションのエントリポイントを自作する」)でApplication.Runメソッドを呼び出すのが普通です。しかし、Form.ShowDialogメソッドを使っても表示することができます。ここでは両者の違いを考えます。
MSDNの「フォームが開始時に非表示になるように設定する」には、次のように書かれています。
「Application.Run() はメッセージ ポンプを起動します。これは、特定のアプリケーションの動作に不可欠なものであり、アプリケーションの有効期間内の特定の時点(シャットダウン時など) におけるフォームの動作に影響を与える場合があります。」
これを読むとApplication.Runでなければならないような気がしますが、不思議なことにこのページのコードにはApplication.Runが一切使われておらず、ShowDialogが使われています。
さらに、Application.RunメソッドについてMSDNの「Application.Run メソッド」では次のように書かれています。
「現在のスレッドで標準のアプリケーション メッセージ ループの実行を開始し、指定したフォームを表示します。通常、アプリケーションの main 関数でこのメソッドを呼び出し、アプリケーションのメイン ウィンドウに渡します。このメソッドは、 Closed イベントのmainForm パラメータにイベント ハンドラを追加します。イベント ハンドラは、 ExitThread を呼び出して、アプリケーションをクリーンアップします。」
以上の説明によれば、「Application.Run(new Form1())」というのは、「Form1を表示させ、Form1が閉じられるまでメッセージループをまわす」ということを意味しており、ウィンドウズアプリケーションには不可欠なものだということになります。
それではApplication.Runを使わずにForm.ShowDialogでメインフォームを表示した時は、メッセージループが回らないというのでしょうか?もちろんそうではありません。「Application.Run vs myForm.ShowDialog - microsoft.public.dotnet.languages.csharp」によると、Form.ShowDialogでメインフォームを表示させた時も、必要があると判断されれば、Application.Runと同様の処理が行われます。つまり、どちらでもあまり変わりません。両者の違いは、Application.Runではメインフォームが閉じられるとExitThreadメソッドが呼び出されることだということです。
補足:メッセージループとは、簡単に言うと、ループ処理によってイベントを監視するための仕組みです。詳しくは、Wikipediaの「メインループ」等をご覧ください。
このような両者の違いを具体的に調べるため、次のようなコードを書いてみました。ここではエントリポイントでForm1をモーダルで表示し、さらにForm1をクリックすることにより、Form2がモードレスで表示されるようにしています。さらに、Mainメソッドから抜ける直前と、Form2が閉じられる時にメッセージボックスが表示されるようにしています。Form1からForm2を表示させ、その後Form1を閉じるとどうなるでしょうか?
''' <summary> ''' アプリケーションのメイン エントリ ポイントです。 ''' </summary> <STAThread()> _ Shared Sub Main() 'フォーム(Form1)のインスタンスを作成 Dim f1 As New Form1 'Clickイベントハンドラを追加 AddHandler f1.Click, AddressOf f1_Click 'フォーム(Form1)を表示 f1.ShowDialog() 'Application.Run(f1); '終了時にメッセージボックスを表示 MessageBox.Show("Application End") End Sub Private Shared Sub f1_Click( _ ByVal sender As Object, ByVal e As EventArgs) 'Form2をモードレスで表示する Dim f2 As New Form2 'Closedイベントハンドラを追加 AddHandler f2.Closed, AddressOf f2_Closed 'Disposedイベントハンドラを追加 AddHandler f2.Disposed, AddressOf f2_Disposed f2.Show() End Sub Private Shared Sub f2_Closed( _ ByVal sender As Object, ByVal e As EventArgs) 'Form2が閉じられる時にメッセージボックスを表示する MessageBox.Show("Form2 Closed") End Sub Private Shared Sub f2_Disposed( _ ByVal sender As Object, ByVal e As EventArgs) 'Form2が破棄される時にメッセージボックスを表示する MessageBox.Show("Form2 Disposed") End Sub
/// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] static void Main() { //フォーム(Form1)のインスタンスを作成 Form1 f1 = new Form1(); //Clickイベントハンドラを追加 f1.Click += new EventHandler(f1_Click); //フォーム(Form1)を表示 f1.ShowDialog(); //Application.Run(f1); //終了時にメッセージボックスを表示 MessageBox.Show("Application End"); } private static void f1_Click(object sender, EventArgs e) { //Form2をモードレスで表示する Form2 f2 = new Form2(); //Closedイベントハンドラを追加 f2.Closed += new EventHandler(f2_Closed); //Disposedイベントハンドラを追加 f2.Disposed += new EventHandler(f2_Disposed); f2.Show(); } private static void f2_Closed(object sender, EventArgs e) { //Form2が閉じられる時にメッセージボックスを表示する MessageBox.Show("Form2 Closed"); } private static void f2_Disposed(object sender, EventArgs e) { //Form2が破棄される時にメッセージボックスを表示する MessageBox.Show("Form2 Disposed"); }
上記のコードを実行させた場合(Form1をShowDialogメソッドで表示させた場合)、Form1からForm2を表示させ、その後Form1を閉じると、「Application End」というメッセージボックスのみが表示されます。つまり、Form1から開いたフォームのClosedやDisposedイベントは発生しません。
一方、Mainメソッドで、「f1.ShowDialog()」を「Application.Run(f1)」に書き換えると、表示されているForm2の数だけ「Form2 Disposed」と表示され、その後、「Application End」というメッセージボックスが表示されます。
つまり、ShowDialogでメインフォームを表示した時は、メインフォームを閉じると、それ以外のモードレスフォームのDisposedイベントが発生することなくアプリケーションが終了してしまいます。よって、ShowDialogでメインフォームを表示した時は、メインフォームを閉じる時に他のすべてのフォームを閉じる処理が必要になるかもしれません。
また、次のようにメインフォームのLoadイベントハンドラ内で別のフォームを表示させるときに、メインフォームをShowDialogメソッドで表示させると、おかしなことになります。
''' <summary> ''' アプリケーションのメイン エントリ ポイントです。 ''' </summary> <STAThread()> _ Shared Sub Main() 'フォーム(Form1)のインスタンスを作成 Dim f1 As New Form1 'Loadイベントハンドラを追加 AddHandler f1.Load, AddressOf f1_Load 'フォーム(Form1)を表示 f1.ShowDialog() 'Application.Run(f1); End Sub Private Shared Sub f1_Load( _ ByVal sender As Object, ByVal e As EventArgs) 'Form1表示時にForm2をモードレスで表示する Dim f2 As New Form2 f2.Show() End Sub
/// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] static void Main() { //フォーム(Form1)のインスタンスを作成 Form1 f1 = new Form1(); //Loadイベントハンドラを追加 f1.Load += new EventHandler(f1_Load); //フォーム(Form1)を表示 f1.ShowDialog(); //Application.Run(f1); } private static void f1_Load(object sender, EventArgs e) { //Form1表示時にForm2をモードレスで表示する Form2 f2 = new Form2(); f2.Show(); }
上記のコードでは、Form1がモーダルフォームとして表示され、Form2の操作が一切できなくなります。Application.RunでForm1を表示させると、このような問題は起こりません。(この問題は.NET Framework 2.0では修正されたようです。)