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

コントロールの配列を作成する

ここでは、幾つかの方法を紹介します。適当な方法を選択してください。

動的にコントロールの配列を作成する

通常は、動的にコントロールオブジェクトの配列を作成するのか最も良い方法でしょう。なお、コントロールを動的に作成する方法は、こちらで説明しています。

フォーム(Form1)にボタンコントロールの配列を作成し、ボタンをクリックするとそのボタンのTextを表示する例を以下に示します。ここではフォームのLoadイベントハンドラでボタンコントロールの配列を作成していますが、コンストラクタの適当な位置で行っても結構です。

VB.NET
コードを隠すコードを選択
'ボタンコントロール配列のフィールドを作成
Private testButtons() As System.Windows.Forms.Button

'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    'ボタンコントロール配列の作成(ここでは5つ作成)
    Me.testButtons = New System.Windows.Forms.Button(4) {}

    'ボタンコントロールのインスタンス作成し、プロパティを設定する
    Me.SuspendLayout()
    Dim i As Integer
    For i = 0 To Me.testButtons.Length - 1
        'インスタンス作成
        Me.testButtons(i) = New System.Windows.Forms.Button
        'プロパティ設定
        Me.testButtons(i).Name = "Button" + i.ToString()
        Me.testButtons(i).Text = i.ToString()
        Me.testButtons(i).Size = New Size(30, 30)
        Me.testButtons(i).Location = New Point(i * 30, 10)
        'イベントハンドラに関連付け
        AddHandler Me.testButtons(i).Click, _
            AddressOf Me.testButtons_Click
    Next i

    'フォームにコントロールを追加
    Me.Controls.AddRange(Me.testButtons)
    Me.ResumeLayout(False)
End Sub

'Buttonのクリックイベントハンドラ
Private Sub testButtons_Click(ByVal sender As Object, _
        ByVal e As EventArgs)
    'クリックされたボタンのNameを表示する
    MessageBox.Show(CType(sender, System.Windows.Forms.Button).Name)
End Sub
C#
コードを隠すコードを選択
//ボタンコントロール配列のフィールドを作成
private System.Windows.Forms.Button[] testButtons;

//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //ボタンコントロール配列の作成(ここでは5つ作成)
    this.testButtons = new System.Windows.Forms.Button[5];

    //ボタンコントロールのインスタンス作成し、プロパティを設定する
    this.SuspendLayout();
    for (int i = 0; i < this.testButtons.Length; i++)
    {
        //インスタンス作成
        this.testButtons[i] = new System.Windows.Forms.Button();
        //プロパティ設定
        this.testButtons[i].Name = "Button" + i.ToString();
        this.testButtons[i].Text = i.ToString();
        this.testButtons[i].Size = new Size(30, 30);
        this.testButtons[i].Location = new Point(i * 30, 10);
        //イベントハンドラに関連付け
        this.testButtons[i].Click +=
            new EventHandler(this.testButtons_Click);
    }

    //フォームにコントロールを追加
    this.Controls.AddRange(this.testButtons);
    this.ResumeLayout(false);
}

//Buttonのクリックイベントハンドラ
private void testButtons_Click(object sender, EventArgs e)
{
    //クリックされたボタンのNameを表示する
    MessageBox.Show(((System.Windows.Forms.Button) sender).Name);
}

上記の例では、Visual Studioのフォームのデザインが使えないという欠点があります。次にフォームのデザインでフォームにボタンコントロールが5つ(Button1〜5)追加されており、それらを配列として扱う例を示します。

VB.NET
コードを隠すコードを選択
'ボタンコントロール配列のフィールドを作成
Private testButtons() As System.Windows.Forms.Button

'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    'ボタンコントロール配列の作成
    Me.testButtons = New System.Windows.Forms.Button(4) {}

    'ボタンコントロールの配列にすでに作成されているインスタンスを代入
    Me.testButtons(0) = Me.Button1
    Me.testButtons(1) = Me.Button2
    Me.testButtons(2) = Me.Button3
    Me.testButtons(3) = Me.Button4
    Me.testButtons(4) = Me.Button5

    'または、次のようにもできる
    'Me.testButtons = New System.Windows.Forms.Button() _
    '    {Me.Button1, Me.Button2, Me.Button3, Me.Button4, Me.Button5}

    'イベントハンドラに関連付け(必要な時のみ)
    Dim i As Integer
    For i = 0 To Me.testButtons.Length - 1
        AddHandler Me.testButtons(i).Click, _
            AddressOf Me.testButtons_Click
    Next i
End Sub

'Buttonのクリックイベントハンドラ
Private Sub testButtons_Click(ByVal sender As Object, _
        ByVal e As EventArgs)
    'クリックされたボタンを表示する
    MessageBox.Show(CType(sender, System.Windows.Forms.Button).Text)
End Sub
C#
コードを隠すコードを選択
//ボタンコントロール配列のフィールドを作成
private System.Windows.Forms.Button[] testButtons;

//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //ボタンコントロール配列の作成
    this.testButtons = new System.Windows.Forms.Button[5];

    //ボタンコントロールの配列にすでに作成されているインスタンスを代入
    this.testButtons[0] = this.Button1;
    this.testButtons[1] = this.Button2;
    this.testButtons[2] = this.Button3;
    this.testButtons[3] = this.Button4;
    this.testButtons[4] = this.Button5;

    //または、次のようにもできる
    //this.testButtons = new System.Windows.Forms.Button[]
    //    {this.Button1, this.Button2, this.Button3, this.Button4, this.Button5};

    //イベントハンドラに関連付け(必要な時のみ)
    for (int i = 0; i < this.testButtons.Length; i++)
        this.testButtons[i].Click +=
            new EventHandler(this.testButtons_Click);
}

//Buttonのクリックイベントハンドラ
private void testButtons_Click(object sender, EventArgs e)
{
    //クリックされたボタンを表示する
    MessageBox.Show(((System.Windows.Forms.Button) sender).Text);
}

すでに配置されているコントロールをリフレクションで検索する

残念ながら以上の方法はVB6ユーザーの方には扱いにくいようで、その後も多くの質問をいただきました。

そこで新たな方法を考えてみました。「フォームに配置されているコントロールを名前で探す」で紹介した方法を使い、「"特定の名前" + "1から連続する数字"」という名前を持つコントロールを配列にするメソッドを作ってみます。このメソッドは、例えばフォームに"TextBox1"、"TextBox2"、"TextBox3"...という名前の複数のTextBoxコントロールがあったとき、"TextBox"という文字列を指定することにより、TextBoxコントロールの配列を返すという機能を持ちます。(ただし、"TextBox1"のインデックスが0になります。また、数字が連続しており、途中が抜けたりしていない必要があります。)

以下にコントロール配列を作成するメソッド"GetControlArrayByName"を示します。

VB.NET
コードを隠すコードを選択
''' <summary>
''' コントロールの配列を取得する
''' </summary>
''' <param name="frm">コントロールのあるフォーム</param>
''' <param name="name">後ろの数字を除いたコントロールの名前</param>
''' <returns>コントロールの配列。
''' 取得できなかった時はnull(VB.NETではNothing)。</returns>
Public Shared Function GetControlArrayByName( _
    ByVal frm As Form, ByVal name As String) As Object
    Dim ctrs As New System.Collections.ArrayList
    Dim obj As Object
    Dim i As Integer = 1
    While True
        obj = FindControlByFieldName(frm, name + i.ToString())
        i += 1
        If obj Is Nothing Then
            Exit While
        Else
            ctrs.Add(obj)
        End If
    End While

    If ctrs.Count = 0 Then
        Return Nothing
    Else
        Return ctrs.ToArray(ctrs(0).GetType())
    End If
End Function

''' <summary>
''' フォームに配置されているコントロールを名前で探す
''' (フォームクラスのフィールドをフィールド名で探す)
''' </summary>
''' <param name="frm">コントロールを探すフォーム</param>
''' <param name="name">コントロール(フィールド)の名前</param>
''' <returns>見つかった時は、コントロールのオブジェクト。
''' 見つからなかった時は、null(VB.NETではNothing)。</returns>
Public Shared Function FindControlByFieldName( _
    ByVal frm As Form, ByVal name As String) As Object
    'まずプロパティ名を探し、見つからなければフィールド名を探す
    Dim t As System.Type = frm.GetType()

    Dim pi As System.Reflection.PropertyInfo = _
        t.GetProperty(name, _
            System.Reflection.BindingFlags.Public Or _
            System.Reflection.BindingFlags.NonPublic Or _
            System.Reflection.BindingFlags.Instance Or _
            System.Reflection.BindingFlags.DeclaredOnly)

    If Not pi Is Nothing Then
        Return pi.GetValue(frm, Nothing)
    End If

    Dim fi As System.Reflection.FieldInfo = _
        t.GetField(name, _
            System.Reflection.BindingFlags.Public Or _
            System.Reflection.BindingFlags.NonPublic Or _
            System.Reflection.BindingFlags.Instance Or _
            System.Reflection.BindingFlags.DeclaredOnly)

    If fi Is Nothing Then
        Return Nothing
    End If

    Return fi.GetValue(frm)
End Function
C#
コードを隠すコードを選択
/// <summary>
/// コントロールの配列を取得する
/// </summary>
/// <param name="frm">コントロールのあるフォーム</param>
/// <param name="name">後ろの数字を除いたコントロールの名前</param>
/// <returns>コントロールの配列。
/// 取得できなかった時はnull(VB.NETではNothing)。</returns>
public object GetControlArrayByName(Form frm, string name)
{
    System.Collections.ArrayList ctrs =
        new System.Collections.ArrayList();
    object obj;
    for (int i = 1;
        (obj = FindControlByFieldName(frm, name + i.ToString())) != null;
        i++)
        ctrs.Add(obj);
    if (ctrs.Count == 0)
        return null;
    else
        return ctrs.ToArray(ctrs[0].GetType());
}

/// <summary>
/// フォームに配置されているコントロールを名前で探す
/// (フォームクラスのフィールドをフィールド名で探す)
/// </summary>
/// <param name="frm">コントロールを探すフォーム</param>
/// <param name="name">コントロール(フィールド)の名前</param>
/// <returns>見つかった時は、コントロールのオブジェクト。
/// 見つからなかった時は、null(VB.NETではNothing)。</returns>
public static object FindControlByFieldName(Form frm, string name)
{
    System.Type t = frm.GetType();

    System.Reflection.FieldInfo fi = t.GetField(
        name,
        System.Reflection.BindingFlags.Public |
        System.Reflection.BindingFlags.NonPublic |
        System.Reflection.BindingFlags.Instance |
        System.Reflection.BindingFlags.DeclaredOnly);

    if (fi == null)
        return null;

    return fi.GetValue(frm);
}

このGetControlArrayByNameメソッドは、配列にしたいコントロールのあるフォームと、配列にしたいコントロールに共通した名前を指定して呼び出します。

補足:GetControlArrayByNameメソッドはフォームクラスのフィールドメンバを調べて配列を返しており、それがコントロールかは調べていませんので、コントロールの配列だけでなく、フォームクラス内のコンポーネントやその他の型のフィールドメンバでも有効です。

以下に具体的な使用法を示します。ここではフォーム(Form1)にテキストボックスコントロール"TextBox1"、"TextBox2"、"TextBox3"と、さらに同じ数のボタンコントロール"Button1"、"Button2"、"Button3"があるものとし、これらのコントロールをGetControlArrayByNameメソッドで配列(textBoxArrayとbuttonArrayフィールド)にしています(インデックス番号は"TextBox1"が0、"TextBox2"が1、"TextBox3"が2となります)。ボタンコントロールをクリックすると、同じインデックスのテキストボックスのTextを表示します。

VB.NET
コードを隠すコードを選択
'テキストボックスコントロール配列のフィールド
Dim textBoxArray() As TextBox
'ボタンコントロール配列のフィールド
Dim buttonArray() As Button

'フォームのLoadイベントハンドラ
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As EventArgs) Handles MyBase.Load
    'テキストボックスコントロールの配列を作成する
    textBoxArray = _
        CType(GetControlArrayByName(Me, "TextBox"), TextBox())
    'ボタンコントロールの配列を作成する
    buttonArray = _
        CType(GetControlArrayByName(Me, "Button"), Button())

    'ボタンをクリックした時にButton_Clickが呼び出されるようにする
    Dim btn As Button
    For Each btn In buttonArray
        AddHandler btn.Click, AddressOf Button_Click
    Next btn
End Sub

Private Sub Button_Click( _
    ByVal sender As Object, ByVal e As EventArgs)
    'クリックされたボタンのインデックス番号を取得する
    Dim index As Integer = -1
    Dim i As Integer
    For i = 0 To buttonArray.Length - 1
        If buttonArray(i).Equals(sender) Then
            index = i
            Exit For
        End If
    Next i

    '押されたボタンと同じインデックスのテキストボックスのTextを表示する
    If index > -1 Then
        MessageBox.Show(textBoxArray(index).Text)
    End If
End Sub
C#
コードを隠すコードを選択
//テキストボックスコントロール配列のフィールド
TextBox[] textBoxArray;
//ボタンコントロール配列のフィールド
Button[] buttonArray;

//フォームのLoadイベントハンドラ
private void Form1_Load(object sender, System.EventArgs e)
{
    //テキストボックスコントロールの配列を作成する
    textBoxArray = (TextBox[]) GetControlArrayByName(this, "TextBox");
    //ボタンコントロールの配列を作成する
    buttonArray = (Button[]) GetControlArrayByName(this, "Button");

    //ボタンをクリックした時にButton_Clickが呼び出されるようにする
    foreach (Button btn in buttonArray)
        btn.Click += new EventHandler(Button_Click);
}

private void Button_Click(object sender, EventArgs e)
{
    //クリックされたボタンのインデックス番号を取得する
    int index = -1;
    for (int i = 0; i < buttonArray.Length; i++)
    {
        if (buttonArray[i].Equals(sender))
        {
            index = i;
            break;
        }
    }

    //押されたボタンと同じインデックスのテキストボックスのTextを表示する
    if (index > -1)
    {
        MessageBox.Show(textBoxArray[index].Text);
    }
}

専用のコンポーネントを使用する

さらに、「VB Helper: HowTo: Make a "control array" component in VB .NET」のようなコンポーネントを使うという方法もあります。

  • 履歴:
  • 2007/9/2 コントロールの名前の先頭に"Button"を追加。
  • 2016/5/30 リンク切れのため、コンポーネントのリンクを変更。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。