ここでは、幾つかの方法を紹介します。適当な方法を選択してください。
通常は、動的にコントロールオブジェクトの配列を作成するのか最も良い方法でしょう。なお、コントロールを動的に作成する方法は、こちらで説明しています。
フォーム(Form1)にボタンコントロールの配列を作成し、ボタンをクリックするとそのボタンのTextを表示する例を以下に示します。ここではフォームのLoadイベントハンドラでボタンコントロールの配列を作成していますが、コンストラクタの適当な位置で行っても結構です。
'ボタンコントロール配列のフィールドを作成 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
//ボタンコントロール配列のフィールドを作成 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)追加されており、それらを配列として扱う例を示します。
'ボタンコントロール配列のフィールドを作成 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
//ボタンコントロール配列のフィールドを作成 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"を示します。
''' <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
/// <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を表示します。
'テキストボックスコントロール配列のフィールド 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
//テキストボックスコントロール配列のフィールド 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」のようなコンポーネントを使うという方法もあります。
(この記事は「.NETプログラミング研究 第31号」で紹介したものを基にしています。)