DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

フォームが一つしか表示されないようにする
VB6と同様にフォームにアクセスできるようにする

Visual Basic 6.0以前のユーザーにとって.NETプログラミングの第一の関門はWindowsフォームの表示に関する問題でしょう。VB6では、デザイナでフォームを作成し、コードで

(フォーム名).Show

とするだけでフォームを表示できました。しかも、同じフォームのShowメソッドを何回呼び出してもフォームが何枚も表示されるということがありません。

これに対して.NETでは、newによりフォームのインスタンスを作成してからShowメソッドを呼び出す必要があり、さらに、「newしてShow」を繰り返していると、それだけ次々とフォームが開かれることになります。

このような事態を避け、VB6のようにフォームを扱えるようにするための方法を紹介します。

.NET Framework 2.0以降のVB.NETで、My.Formsを使用する方法

.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のコンストラクタをプライベートに変更すればさらに確実です。具体的なコードは、下の「シングルトンデザインパターンの適用」をご覧ください。
VB.NET
コードを隠すコードを選択
'ただ一つのフォームのインスタンスを保持するフィールド
Private Shared _instance As Form2

'ただ一つのフォームにアクセスするためのプロパティ
Public Shared ReadOnly Property Instance() As Form2
    Get
        '_instanceがnullまたは破棄されているときは、
        '新しくインスタンスを作成する
        If _instance Is Nothing OrElse _instance.IsDisposed Then
            _instance = New Form2
        End If
        Return _instance
    End Get
End Property
C#
コードを隠すコードを選択
//ただ一つのフォームのインスタンスを保持するフィールド
private static Form2 _instance;

//ただ一つのフォームにアクセスするためのプロパティ
public static Form2 Instance
{
    get
    {
        //_instanceがnullまたは破棄されているときは、
        //新しくインスタンスを作成する
        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プロパティを加えます。

VB.NET
コードを隠すコードを選択
'基本クラスとして使用できないようにする
Public NotInheritable Class Form2
    Inherits System.Windows.Forms.Form

    'コンストラクタをPrivateにする
    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
C#
コードを隠すコードを選択
//基本クラスとして使用できないようにする
public sealed class Form2 : System.Windows.Forms.Form
{
    //コンストラクタをPrivateにする
    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イベントを使用してください。
VB.NET
コードを隠すコードを選択
'フォームを閉じずに隠すようにする
Protected Overrides Sub OnClosing( _
    ByVal e As System.ComponentModel.CancelEventArgs)
    e.Cancel = True
    Me.Hide()
End Sub
C#
コードを隠すコードを選択
//フォームを閉じずに隠すようにする
protected override void OnClosing(CancelEventArgs e)
{
    e.Cancel = true;
    this.Hide();
}
  • 履歴:
  • 2007/2/24 My.Formsに関する記述を追加。
  • 2012/3/12 My.Formsの方法は別のスレッドからアクセスする場合は使えない説明を追加。