フォームが一つしか表示されないようにする
VB6と同様にフォームにアクセスできるようにする
Visual Basic 6.0以前のユーザーにとって.NETプログラミングの第一の関門はWindowsフォームの表示に関する問題でしょう。VB6では、デザイナでフォームを作成し、コードで
(フォーム名).Show
とするだけでフォームを表示できました。しかも、同じフォームのShowメソッドを何回呼び出してもフォームが何枚も表示されるということがありません。
これに対して.NETでは、newによりフォームのインスタンスを作成してからShowメソッドを呼び出す必要があり、さらに、「newしてShow」を繰り返していると、それだけ次々とフォームが開かれることになります。
このような事態を避け、VB6のようにフォームを扱えるようにするための方法を紹介します。
.NET Framework 2.0以降のVB.NETでは、My.Formsを使用するのが良いでしょう。
例えば、「Form2」という名前のフォームにアクセスするには、「My.Forms.Form2」というプロパティにアクセスします。Form2を表示させたければ、「My.Forms.Form2.Show()」とすればよいのです。「My.Forms.Form2.Show()」を何回呼び出しても、Form2は1枚しか表示されません。
Form2に配置された"TextBox1"のTextを取得したいのであれば、「My.Forms.Form2.TextBox1.Text」から取得できます。
「My.Forms.Form2」をもっと簡単に、「Form2」とだけすることもできます。「Form2.Show()」でForm2が表示されます。
「My.Forms.Form2 = Nothing」とすることにより、Form2を破棄することができます。Form2が閉じていなければ、閉じられます。
My.Formsでアクセスできるフォームは、同じプロジェクト内にあるフォームだけです。
補足:"Form2"という名前のフォームが複数存在するときは、フォーム名の前に名前空間を付け加える必要があります。
ただしMy.Formsはスレッド毎にフォームのインスタンスを作成しますので、別のスレッドからも1つのフォームにアクセスする場合は、この方法は使えません。
静的プロパティを使用する方法
フォームのインスタンスは静的フィールドに保持しておき、静的プロパティでこのフィールドがnullまたは破棄(Dispose)されているときに新しいインスタンスを作成するようにします。
次に簡単なサンプルを示します。Form2というフォームクラスがあるものとし、以下のような_instanceフィールドとInstanceプロパティを加えます。
補足:これに加え、Form2クラスをシールクラスとし、Form2のコンストラクタをプライベートに変更すればさらに確実です。具体的なコードは、下の「シングルトンデザインパターンの適用」をご覧ください。
Private Shared _instance As Form2
Public Shared ReadOnly Property Instance() As Form2
Get
If _instance Is Nothing OrElse _instance.IsDisposed Then
_instance = New Form2
End If
Return _instance
End Get
End Property
private static Form2 _instance;
public static Form2 Instance
{
get
{
if (_instance == null || _instance.IsDisposed)
_instance = new Form2();
return _instance;
}
}
Form2のインスタンスにアクセスするには、Form2.Instance静的メソッドを使用します。例えば、Form2をモードレスで表示するには、
Form2.Instance.Show()
とするだけです(名前空間が異なる場合は、適当な名前空間を付加してください)。Form2.Instance.Show()を何回呼び出しても、フォームが何枚も表示されることはありません。
補足:このプロパティはスレッドセーフではありませんが、それほど問題にならないでしょう。しかしどうしてもスレッドセーフにしたいということであれば、下の例を参考にしてください。
シングルトンデザインパターンの適用
ところで、この問題の解決に、シングルトン(Singleton)デザインパターンがそのまま使えるのではないかと考える方もいらっしゃるかもしれません。シングルトンとは、クラスのインスタンスがただ一つであることを保障するためのデザインパターンで、フォームのインスタンスがただ一つであることを保障するということであれば、これはまさに「ビンゴ」と言えるでしょう。
シングルトン及び、C#におけるシングルトンの実装に関しては、次のページが参考になります。
ここでは「C# でのシングルトンの実装 」の「静的な初期化」で紹介されている方法を使用します。この方法によりインスタンスが一つであるForm2フォームを作成するには、まずForm2クラスをシールクラスとし、Form2のコンストラクタをprivateに変更し、以下のような_instanceフィールドとInstanceプロパティを加えます。
Public NotInheritable Class Form2
Inherits System.Windows.Forms.Form
Private Sub New ()
End Sub
Private Shared _instance As New Form2
Public Shared ReadOnly Property Instance() As Form2
Get
Return _instance
End Get
End Property
End Class
public sealed class Form2 : System.Windows.Forms.Form
{
private Form2()
{
}
private static readonly Form2 _instance = new Form2();
public static Form2 Instance
{
get
{
return _instance;
}
}
}
ところが上記のようにフォームクラスにシングルトンパターンをそのまま適用すると、うまく行きません。フォームを閉じてから再びフォームを開こうとすると、エラーが発生するのです。というのは、フォームのインスタンスが一つであっても、そのフォームが破棄されてしまったらそのインスタンスを使ってフォームを開くことができなくなってしまうからです。
この問題を解決するには、一つの方法として、フォームが破棄されないようにフォームが閉じられないようにするといった対策が考えられます。
例えば、Form2クラスに次のコードを書き加えることにより、フォームを閉じようとした時に閉じずに、隠すようになります。
注意:.NET Framework 2.0以降では、FormクラスのClosing、Closedイベントの代わりに、FormClosing、FormClosedイベントを使用してください。
Protected Overrides Sub OnClosing( _
ByVal e As System.ComponentModel.CancelEventArgs)
e.Cancel = True
Me .Hide()
End Sub
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true ;
this .Hide();
}
履歴:
2007/2/24 My.Formsに関する記述を追加。
2012/3/12 My.Formsの方法は別のスレッドからアクセスする場合は使えない説明を追加。
(この記事は、「.NETプログラミング研究 第42号 」で紹介したものです。)