Visual Basic 初級講座 |
Visual Basic 中学校 > 初級講座 >
第20回 コントロールをまとめる方法
複数のコントロールに同じように命令したりプロパティを設定する場合、1つ1つのコントロールに対する命令をいちいち書かなければいけないわけではありません。今回はコントロールをグループ化して一括して命令する方法を紹介します。
この回の要約 ・Controlsプロパティを使うとすべての子コントロールにアクセスできる。 ・次のように書くと、すべての子コントロールに命令できる。
・VB.NET2002, VB.NET2003でも、名前を使ってアクセスできるコントロールのコレクション化を紹介 ・孫コントロール等、すべての子孫コントロールにアクセスできるコントロールのコレクション化を紹介 |
画面を表示するアプリケーション、つまりほとんどのアプリケーションではテキストボックやボタンなどさまざまなコントロールを使用します。
これらのコントロールを制御するプログラムは1つ1つのコントロールに個別に命令しなければいけないのでしょうか?もちろん、そんなことありません。
たとえば、10個のテキストボックスがあるフォームで、10個のテキストボックスすべてのEnabledをFalseにしたい場合を考えて見ましょう。次のように書けば目的は達成できますが、手間がかかります。
TextBox1.Enabled =
False TextBox2.Enabled = False TextBox3.Enabled = False TextBox4.Enabled = False TextBox5.Enabled = False TextBox6.Enabled = False TextBox7.Enabled = False TextBox8.Enabled = False TextBox9.Enabled = False TextBox10.Enabled = False |
■リスト1
一方、次のようにするとフォーム上にあるテキストボックスにまとめて命令することができます。
Dim o
As Control For Each o In Me.Controls If o.GetType Is GetType(TextBox) Then o.Enabled = False End If Next |
■リスト2
この例にはまだ紹介していない手法が使われていますので、ごく簡単に補足します。
For Each 〜 Nextはループの一種で、使い方もそれほど難しくはないので今はコードのサンプルの形をそのまま覚えていただければ十分です。
GetTypeメソッド(読み方:GetType = ゲットタイプ)はコントロールや変数の型を調べるときに良く使われます。たとえば、変数Xに対して、MsgBox(X.GetType)とすると、Xの型が表示できます。XがIntegerかどうか調べるには、If X.GetType Is GetType(Integer)とします。上記の例では変数oがTextBoxかどうかを調べています。
さて、今回はコントロール1つ1つを個別に制御するのではなく、この例のようにまとめて制御する手法を紹介します。
なお、コントロールを個別に制御すべきか、まとめて制御すべきかは状況により異なるということも忘れないで下さい。いつでもまとめて制御すればよいと言うものでもありません。
コントロールをまとめて制御するときに最もよく利用するのが、Controlsプロパティ(読み方:Controls = コントロールス)です。Controlsプロパティはすべてのコントロールが持っているプロパティで、Controlsプロパティを使うと そのコントロール上に配置されているすべてのコントロールにアクセスすることができます。
ただし、コントロールの中にパネルなどのコンテナがある場合、その中にあるコントロールにはアクセスできません。
たとえば次の画像を見てください。
■画像1
このフォームの中ではパネルの中にテキストボックスとボタンが配置されていますが、フォームのControlsプロパティではこれらのコントロールにアクセスできません。これらのコントロールにアクセスす るにはパネルのControlsプロパティを使用する必要があります。
■画像2
このフォームの構造を図解すると、上の図に通りになります。Controlsプロパティでは子コントロールにはアクセスできますが、孫コントロールにはアクセスできない点を覚えておいてください。つまり、FormのControlsプロパティでは、TextBox3にはアクセスできませんが、Panel1のControlsプロパティはTextBox3にアクセスできます。
さて、 実際にControlsプロパティを使って個々のコントロールにアクセスするには引数にコントロール固有の数値、またはコントロール名を指定します。ただし、コントロール名を指定できるのはVB2005以降ですので、それ以前のVBでは数値しか指定できません。
この「コントロール固有の数値」はVBが自動的に個々のコントロールに割り当てた数値です。どのような数値が割り当てられるのかは実行するまでわかりません。そのため、具体的な数値を使ってControlsプロパティを使用する場面はあまりありません。
以上を簡単にまとめると次のとおりになります。
・Controlsプロパティを使うと子コントロールにアクセスできる。 ・Controlsプロパティを使っても孫コントロールにはアクセスできない。 ・Controlsプロパティを使って個々のコントロールにアクセスするには、コントロール固有の数値か、コントロールの名前を指定する。
|
孫コントロールにアクセスしたり、VB.NET2002,VB.NET2003でも名前を使ってアクセスしたりする方法は一番最後に紹介します。
Controlsプロパティの具体的な使用例を紹介しましょう。いくつかのコントロールを適当に配置したフォームで、次の命令を実行してみてください。どれかのコントロールが非表示(Visible = False)になります。
Me.Controls(2).Visible = False |
■リスト3
どのコントロールが非表示になるか、わからないのでこのままでは何の役にも立ちません。あくまでControlsプロパティの使い方を説明するため だけの例です。
しかし、次のようにするとすべてのコントロールを非表示にできます。このように「すべて」に対して命令できるのであればいろいろと使い道もでてきます。
Dim
oControl As
Control For Each oControl In Me.Controls oControl.Visible = False Next |
■リスト4
この例はFor Each 〜 Nextではなく、通常のFor 〜 Nextを使って書くこともできます。その例は下の通りです。
Dim K
As
Integer
For K = 0
To
Me.Controls.Count - 1
Me.Controls(K).Visible = False Next |
■リスト5
博士のワンポイントレッスン
|
上の例を少し改造して次のようにすると、さきほど説明した「VBが自動的に個々のコントロールに割り当てた数値」を簡単に見ることもできます。
Dim K
As
Integer
For K = 0
To
Me.Controls.Count - 1
Me.Controls(K).Text = K Next |
■リスト6
とはいえ、実際には本当に「すべて」のコントロールに一括して命令する場面はそれほど多くありません。最も需要が高いのは、「いくつかのコントロールをまとまりとして、そのまとまりに対して一括して命令する」という機能でしょう。これについては章を改めて 順番に説明します。
Controlsプロパティによってコントロールをグループ化する主な方法を紹介しますが、先にも書いたようにControlsプロパティを使う方法では孫コントロールにはアクセスできない点に注意してください。
いくつかのコントロールをひとまとまりにして命令する最も簡単な方法は、Controlsプロパティと「コンテナ」を合わせて使うことです。「コンテナ」は以前にも登場しましたが、フォームやパネルのようにコントロールを配置できるコントロールのことでした。最も手軽なコンテナはパネルです。
フォーム上にパネルを配置して、まとめたいコントロールをそのパネルの中に配置してください。
あとは、先ほど説明したのと同じ手法でパネル内のコントロールすべてを制御することができます。たとえば、パネル内のすべてのコントロールのTextを「こんにちは!」にするには次のようにします。
Dim
oControl As
Control For Each oControl In Panel1.Controls oControl.Text = "こんにちは!" Next |
■リスト7
この方法は手軽でわかりやすい方法ですが、コントロールの位置が離れている場合はつかいにくくなります。たとえば、フォームの端と端にあるコントロールをまとめたいが、中央にあるコントロールはまとめたくない場合に、無理に端のコントロールを一つのコンテナの中に配置するのは少々強引でしょう。そのような配置はレイアウトがわかりにくくなって後々混乱を招きます。
あらかじめコントロールの名前に共通する特徴を持たせておけば、その特徴のあるコントロールをまとめて扱うことができます。
たとえば、何かの登録画面があって、いくつかの項目には必ずなにかを入力しなければいけないとしましょう。その「いくつかの項目」をまとまりとして扱いたい場合、それらの項目を表すテキストボックスに共通して、「txtNeed」から始める名前をつけておきます。「txtNeedName」や「txtNeedMailAddress」といった具合です。
これだけで、これら「txtNeed」から始まるコントロールをグループ化できます。
次の例では「txtNeed」から始まる名前のコントロールの背景色を薄い赤にします。
Dim
oControl As
Control For Each oControl In Me.Controls If
Strings.Left(oControl.Name, 7) = "txtNeed"
Then |
■リスト8
この方法は手軽ですが、「共通の特徴を持った名前」という目に見えない規則に縛られることになります。この手の「目に見えない規則」は始めのうちは甘く見て手軽に使ったりもするのですが、無計画に使っていると後になって気が付いたときには規則が増えたり複雑化して手に終えなくなってしまう場合があります。そうなってから元のシンプルな状態に戻すのは結構しんどいです。あまり多用しないように注意が必要です。特にシンプルを愛している方は使わない方が良いかもしれません。
冒頭の例でも紹介しましたが、コントロールの種類、つまり「型」でグループ化することもできます。同じ型のコントロールをまとめて制御すると言うわけです。
次の例はフォーム上のすべてのボタンを使用不可にします。
Dim
oControl As
Control For Each oControl In Me.Controls If
oControl.GetType Is GetType(Button)
Then |
■リスト9
この方法は違う種類のコントロールはグループ化できないので使える場面は限られることになります。また、この方法でプログラムしてしまうと、後になって「例外的にこのコントロールはグループ化しない」という修正が必要になった場合に、プログラムの修正が煩雑になってしまいます。
名前にしろ種類(型)にしろ、本来別の用途のものを転用してグループ化の根拠とするのはプログラムがわかりにくくなるのでやりたくないという方もいらっしゃるでしょう。
簡単にできるグループ化の最後の手段はTagプロパティ(読み方:Tag = タグ)によるグループ化です。Tagプロパティは何の機能もないプロパティで、まさにこういったユーザーが自由に何かを設定したい場合に使うプロパティです。
グループ化したいコントロールに共通の特徴を持つTagプロパティを設定しておけば、For Eachのループの中でそのコントロールだけに命令することができるわけです。
たとえば、ユーザーが管理者の時だけ使えるいくつかのコントロールのTagプロパティに"Admin"と設定しておきます。そうすると、以下のプログラムでは管理者用のすべてのコントロールのEnabledをFalseにします。
Dim
oControl As Control For Each oControl In Me.Controls If
oControl.Tag = "Admin"
Then |
■リスト10
この方法は非常に柔軟でほとんどの場合で有効です。Tagプロパティの文字列を工夫したり、Tagプロパティに独自のクラスをセットすることで応用範囲は広がります。(TagプロパティはObject型なので文字列でなくても何でも設定できます)。
Controlsプロパティに頼らないでグループ化を行うこともできます。自分でグループ化したいコントロールのコレクションまたは配列を定義する方法です。「コレクション」や「配列」は初級講座ではまだ登場していません。詳細は別の機会に解説する予定ですがどちらも似たようなものをまとめる機能です。実際、Controlsプロパティはコレクションの一種です。ここではこの辺りのことは特に気にしないで結構ですし、必要があれば以下のプログラムを真似することも簡単です。
さて、次のようにプログラムするとプログラムから自由な組み合わせでコントロールをグループ化することができます。
Dim
MyControls As
New ArrayList Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load MyControls.Add(TextBox2) End Sub |
■リスト11
この例では、TextBox2, TextBox3, Button1をグループ化しました。以降このグループについてはMyControlsを使うことによりControlsプロパティと似た方法でアクセスすることができます。
たとえば、このグループに属するすべてのコントロールのEnabledをFalseにするには次のように書きます。
Dim
oControl As Control For Each oControl In MyControls oControl.Enabled = False Next |
■リスト12
この方法では非常に柔軟なグループ化を行うことができますが、プログラムでグループを定義しなければならないので作業が面倒ですし、ミスも心配です。しかしながら現在のところこの方法こそが定番のグループ化方法と言えるでしょう。
以上で通常の解説は終了です。ここまでの知識があればコントロールのグループ化に関してはまったく問題ないでしょう。
ここでは、もう少しステップアップして独自のコレクションを使ったより便利なグループ化の実例を紹介します。
コントロールのグループ化に関してよく言われる問題はControlsプロパティで番号ではなく名前でコントロールを区別したいと言うことと、コンテナの中のコントロールも含めて本当にすべてのコントロールにアクセスしたいと言うことです。
ちょっとした工夫でこの2点の問題は解決できますが、解決方法を紹介する前に「Controlsプロパティで名前を使ってコントロールを区別したい」というのはどういうことなのか簡単に説明します。
VB2005以降ではこの問題は改善されているので次のようなプログラムを書くことができます。
Dim
i As
Integer Dim ControlName As String For i = 1 To 5 ControlName = "ComboBox" & i Me.Controls(ControlName).Enabled = False Next |
■リスト13
このプログラムではComboBox1 〜 ComboBox5 のEnabledがFalseになります。
VB.NET2002およびVB.NET2003ではControlsプロパティでは名前が使用できないのでこのような書き方をすることができません。
これらの問題点を解決するために自作のコレクションを作成します。以下で紹介するCreateControlCollection関数を呼び出すとフォーム上のコンテナ内部も含んだすべてのコントロールが名前でアクセス可能になります。
Public
Shared Function
CreateControlCollection(ByVal Container
As Control) As
Hashtable Dim Hash As New Hashtable Call AppendControlCollection(Container, Hash) Return Hash End Function |
Private Shared
Sub AppendControlCollection(ByVal
Container As Control,
ByVal Hash As Hashtable) For Each oControl As Control In Container.Controls
If oControl.Controls.Count > 0
Then Hash(oControl.Name) = oControl Next End Sub |
■リスト14
この関数はこれで完成していますので、必要な場合はここからそのままコピー貼り付けして使用してください。
この関数の使用例も紹介しておきます。
次の例では、コンテナの中のコントロールも含めてフォーム上のすべてのコントロールのTextをHello!にします。
Dim
MyControls As Hashtable =
CreateControlCollection(Me) Dim oControl As Control For Each oControl In MyControls.Values oControl.Text = "Hello!" Next |
■リスト15
次の例では、コンテナの中のコントロールも含めてTextBox1 〜 TextBox12のTextをHello!にします。
コントロール名を名前で指定していますが、VB.NET2002, VB.NET2003でも使用可能です。
Dim
MyControls As Hashtable =
CreateControlCollection(Me) Dim i As Integer For i = 1 To 12 MyControls("TextBox" & i).text = "Hello!" Next |
■リスト16