Visual Basic クラスライブラリ詳解
VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応

 

Visual Basic 中学校 > クラスライブラリ詳解 > System > Windows > Forms >

ListBox

完全限定名 System.Windows.Forms.ListBox
基底クラス System.Object > System.MarshalByRefObject > System.ComponentModel.Component > System.Windows.Forms.Control > System.Windows.Forms.ListControl
インターフェイス IBindableComponent, IComponent, IDisposable, IDropTarget, ISyncronizeInvoke, IWin32Window
派生クラス System.Windows.Forms.CheckedListBox
使用頻度 B
バージョン .NET Framework 1.0以上 (VB.NET2002以上)
主な機能 文字列の一覧を表示する。

 

1.概要

 

ListBoxクラスは文字による一覧を表示するためのコントロールです。通常はFormなどのコンテナに貼り付けて使用します。コントロールであるので、ListBoxクラスというよりもListBoxコントロールと呼ばれることが多いです。

■画像1:ListBox

ListBoxは一覧を表示するための最もシンプルなコントロールです。基本的には1列しか表示できません。表が必要な場合にはListBoxではなくDataGridViewなどの使用を検討します。 その他のListBoxに似たコントロールについては下の囲み記事を参照してください。

ユーザーはListBoxに表示された1つまたは複数の項目を選択することができます。しかし、直接入力して値を変更したり削除することはできません。プログラムで制御する場合は値の変更や削除は簡単に行えます。

メモ メモ  -  ListBoxに似たコントロール

上述のようにListBoxはシンプルな一覧表示を行うコントロールです。ここではListBoxに似た機能を持つコントロールを紹介しますのでListBoxでは難しい仕様を実現しようとしている場合に検討してみてください。

ComboBox 一覧を常に表示するのではなく、ドロップダウン形式で必要な時だけ表示します。また値を入力することができます。
CheckedListBox ListBoxの各項目の左にチェックボックスがついているものです。一覧から項目を明示的に選択することが重要である場合に使用します。
DataGridView VB2005以降。単一列のリストではなく複数の列を表形式で表示するときに使用します。値の入力もできます。

■表1:ListBoxに似たコントロール

 

2.一覧の作成

 

2−1.プロパティウィンドウによる一覧の設定

ListBoxの一覧は通常Itemsプロパティによって制御されます。一覧の項目を編集するにはデザイン時にプロパティウィンドウを使用するか、プログラムでItemsプロパティにアクセスします。

プロパティウィンドウでは文字列コレクションエディタを使用してItemsプロパティを編集することができます。

■画像2:文字列コレクションエディタ

このエディタ上の1行が一覧の項目の1つになります。

ListBoxのスマートタグを使用して一覧を編集することもできます。ListBoxのスマートタグを表示するにはListBoxを選択した状態で右上に表示される小さな三角形のボタンをクリックして、「項目の編集」を選びます。

■画像3:ListBoxのタスク

この場合でもプロパティウィンドウと同様に文字列コレクションエディタが表示されます。

 

2−2.プログラムからの一覧の追加

プログラムで制御する場合はItemsプロパティのAddメソッドまたはInsertメソッドまたはAddRangeメソッドを使用して項目を追加します。一覧の中で項目が追加される位置はSortedプロパティによって異なります。SortedプロパティがTrueに設定されている場合は項目の順序は自動的に辞書順になりますので、項目を追加する位置には特に意味がありません。 以下ではSortedプロパティがFalseであることを前提に位置を説明します。

項目の位置のことは「インデックス」と表現する場合もあります。項目の位置とインデックスは同じ意味で、先頭の項目のインデックスは0です。

ただし、日本語の表現の問題で「位置」と言った場合には先頭を1番目と数える場合があります。インデックスは必ず先頭は0です。

 

Addメソッドでは1度に1つの項目を追加することができます。Insertメソッドは位置を指定して一度に1つの項目を追加できます。AddRangeメソッドでは1度に複数の項目を追加できます。 また、Countプロパティを使用するとすべての項目の数を取得することができます。

メソッド 機能
メソッド Items.Add 一覧の末尾に項目を1つ追加する。
プロパティ Items.Count すべての項目の数を取得する。
メソッド Items.Insert 位置を指定して項目を1つ追加する。
メソッド Items.AddRange 一覧の末尾に項目を複数追加する。

■表2

 

次の例はAddメソッドを使用してListBox1に3つの項目を追加します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.Items.Add("あさがお")
ListBox1.Items.Add("いぬ")
ListBox1.Items.Add("うま")

■リスト1

 

次の例はAddメソッドを使用して3つの項目を追加した後で、Insertメソッドを使用して2番目に項目を挿入します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.Items.Add("あさがお")
ListBox1.Items.Add("いぬ")
ListBox1.Items.Add("うま")
ListBox1.Items.Insert(1, "★挿入★")

■リスト2

項目の位置ははじめの項目が0なので、2番目に挿入するには1を指定します。

Insertメソッドのようにインデックスを指定するメソッドやプロパティでは存在しないインデックスを指定すると例外ArgumentOutOfRangeExceptioinが発生するので常に注意してください。

次の例は1000番目が存在しないのでエラーになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.Items.Add("あさがお")
ListBox1.Items.Add("いぬ")
ListBox1.Items.Add("うま")
ListBox1.Items.Insert(999, "★挿入★") '←ここでエラー

■リスト3

 

次の例はAddRangeメソッドを使用してListBox1に3つの項目を追加します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.AddRange(New String() {"あさがお", "いぬ", "うま"})
 

■リスト4

AddRangeメソッドは配列かListBox.ObjectCollectionクラスを引数にとります。配列を使用した次のようなプログラムも有効です。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Values(2) As String

Values(0) =
"あさがお"
Values(1) = "いぬ"
Values(2) = "うま"

ListBox1.Items.AddRange(Values)

■リスト5

 

2−3.効率的な項目の追加方法

AddメソッドやInsertメソッドを使用して大量の項目を追加するとかなり処理に時間がかかる場合があります。これは項目を1つ追加するたびに再描画が行われるためです。一度に大量の項目を追加する場合はすべての項目の追加が完了した時点で1回だけ再描画を行えばよいことがほとんどなのでその都度再描画するのは非効率です。

BeginUpdateメソッドを使用すると再描画のタイミングを制御できるので、1つずつ項目を追加したときの処理速度の問題を解決できます。BeginUpdateメソッドを使用するとEndUpdateメソッドが呼び出されるまで再描画を行いません。

次の例はBeginUpdateメソッドを使用しないで大量の項目を追加します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim StartTime As Double = DateAndTime.Timer

For i As Integer = 0 To 10000
    ListBox1.Items.Add(i)
Next

MsgBox(DateAndTime.Timer - StartTime & "秒かかりました。")

■リスト6

処理の開始前に時間を記録しておくようにしてあるので、この処理にかかる時間がわかります。

処理の前後でBeginUpdateEndUpdateを使用すると圧倒的に速度が向上します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim StartTime As Double = DateAndTime.Timer

ListBox1.BeginUpdate()

For
i As Integer = 0 To 10000
    ListBox1.Items.Add(i)
Next

ListBox1.EndUpdate()

MsgBox(DateAndTime.Timer - StartTime & "秒かかりました。")

■リスト7

BeginUpdateを呼び出した後でEndUpdateを呼び出さないとListBoxには何も表示されないので注意してください。

なお、AddRangeメソッドを使用する場合は再描画は1度しか行われないのでBeginUpdateメソッドとEndUpdateメソッドを使用する必要はありません。

 

2−4.項目の数

全ての項目の個数を調べるにはItems.Countプロパティを使用します。このプロパティはごく簡単に使用できます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Count As Integer

Count = ListBox1.Items.Count

MsgBox(Count & "個の項目があります。")

■リスト8

 

3.一覧の要素

 

3−1.文字列以外の項目

ListBoxの一覧の個々の要素は通常は文字列を指定しますが、実際にはあらゆる種類のオブジェクトを一覧に追加することができます。 ただし文字列以外のものを追加すると処理が複雑になるので通常はお勧めできません。

次の例は有効です。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.Items.Add(Me)
ListBox1.Items.Add(Button1)
ListBox1.Items.Add(Now)

■リスト9

この例を実行するとListBox1には次のように表示されます。

■画像4:文字列以外の物をItemに追加

ListBoxはどのような種類のオブジェクトであれ文字列に変換して表示しようとします。文字列に変換するためにそれぞれのオブジェクトのToStringメソッドを呼び出すのでこのような結果になります。

Me.ToStringおよびButton1.ToStringは何も返 さないので、表示は空欄になります。Now.ToStringは現在の日付を返すのでこの分だけ表示されます。

 

3−2.表示内容の制御

DisplayMemberプロパティを使用すると文字列に変換するのではなく、指定したプロパティの値を表示させることができます。たとえば、それぞれのオブジェクトのTextプロパティの値を一覧に表示させるにはDisplayMember"Text"をセットします。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.DisplayMember = "Text"
ListBox1.Items.Add(Me)
ListBox1.Items.Add(Button1)
ListBox1.Items.Add(Now)

■リスト10

この例ではListBox1の表示は次のようになります。

■画像5:DisplayMember設定後

この例ではNowにはDisplayMemberで設定したTextプロパティが存在 しません。この場合には文字列変換にToStringメソッドが使用されます。

 

3−3.項目と表示内容の関係

オブジェクトを一覧に追加した場合、表示上はすべて文字列になりますが一覧の内容としてはそのオブジェクトを保持し続けます。

たとえば、一覧にButton1を追加した後で、ItemsプロパティからButton1を取得することも可能です。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.DisplayMember = "Text"
ListBox1.Items.Add(Me)
ListBox1.Items.Add(Button1)
ListBox1.Items.Add(Now)

Dim
B As Button

B = ListBox1.Items(1)
B.BackColor = Color.Red

■リスト11

 

以上のことからListBoxの1つの項目は単純に表示されている文字列の他に、位置を表すインデックスと項目自体を保持していることがわかります。この3つの区別が正確にできないとListBoxを制御するときに意図したとおりの値の設定・取得ができないということになります。ただし、ListBoxには常に文字列しか追加しないというのであれば特に区別する必要はありません。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.DisplayMember = "Text"
ListBox1.Items.Add(Me)
ListBox1.Items.Add(Button1)
ListBox1.Items.Add("あさがお")

■リスト12

このプログラムを実行した場合、内部では次のように値が保持されます。

インデックス 表示されている文字列の例 項目
0 "Form1" Form1のインスタンス
1 "Button1" Buttonのインスタンス
2 "あさがお" "あさがお"

■表3

先ほどから何度も登場しているItemsプロパティは項目のコレクションであって、表示されている文字列のコレクションでない点に注意してください。

これらの関係を図にまとめると次のようになります。

■図1

この図にどのメソッド・プロパティがListBoxの一覧の何を対象にしているのかを示してます。後で説明するメソッドやプロパティについてもこの図に書き込んであります。

たとえば、Textプロパティは「表示」の下に書いてあるので表示内容を示すプロパティであることがわかります。Itemsプロパティは「項目」の下に書いてあるので項目を示すプロパティであることがわかります。つまり、ListBox1.Items(0)で取得できるのは表示されている文字列ではなく、追加された項目ということです。

また、Itemsが「項目」の下に書いてあるので、Items.AddItems.InsertなどItemsのメンバはすべて表示ではなく項目に関するものということになります。

ただし、若干の例外があるのでこの図がすべてではありません。

表示欄ではダブルクォーテーション付きで「"Form1"」となっているのが項目欄ではダブルクォーテーションなしで「Form1」となっている点も注意してください。

また、インデックス・表示・項目の相互の関係については矢印で示してあります。たとえば、項目から表示を取得するにはGetItemTextメソッドを使用することがわかります。DisplayMemberプロパティも項目から表示を取得しますが、こちらはシステムが内部で使用する設定であってプログラム中直接DisplayMemberを使用して表示を取得することはできません。こういった細かいことはこの図では表現されていません。

あとあと表示や項目の関係について混乱してきたらこの図を見直してみてください。

 

なお、ListBoxに文字列型の値した追加しない場合は、表示されている文字列と項目は常に一致しているので単純に考えることができます。ですから、特に理由がない限りListBoxには文字列だけを追加することをお勧めします。

 

4.項目の削除

 

項目を削除に使用する主なメソッドは次の通りです。

メソッド・プロパティ 説明
メソッド Items.Clear 全ての項目の削除します。
メソッド Items.Remove 指定した項目を削除します。
メソッド Items.RemoveAt 指定した位置にある項目を削除します。

■表4

Clearメソッドはすべての項目を削除してListBoxをまっさらな状態にします。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.Clear()
 

■リスト13

 

Removeメソッドは指定した項目を削除します。全ての項目が文字列である場合は単純にその文字列を指定して項目を削除することができます。該当項目が複数ある場合は一番先頭に近い項目だけを削除します。

次の例では「いぬ」という文字列の項目を削除します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.Remove("いぬ")
 

■リスト14

次の例はButton1である項目を削除します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.Remove(Button1)
 

■リスト15

対の例では「Button1」という文字列の項目を削除します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.Remove("Button1")
 

■リスト16

項目を追加するときにListBox1.Items.Add(Button1)とした場合には、ButtonクラスのインスタンスであるButton1を項目に追加しているので、ListBox1.Items.Remove(Button1)で削除することができます。ListBox1.Items.Remove("Button1")で削除することはできません。

逆にListBox1.Items.Add("Button1")とした場合には、「Button1」という文字列を項目に追加しているので、ListBox1.Items.Remove("Button1")で削除することができます。ListBox1.Items.Remove(Button1)では削除できません。

これは表示されている文字列と項目の実際の値の違いです。

 

RemoveAtメソッドはインデックスを指定して項目を削除します。

次の例では2番目の項目を削除します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.Items.RemoveAt(1)
 

■リスト17

 

 

5.項目の選択

 

5−1.選択の動作

ListBox内の項目はマウスおよびキーボードの操作で選択することができます。初期状態では1度に選択可能な項目は1つだけですが、SelectionModeプロパティを設定することで1度に複数の項目を選択できるようにできますし、逆に全く項目を選択できないようにもできます。

SelectionModeプロパティに設定可能な値とその意味は次の通りです。

機能
列挙体の値 MultiExtended 1度に複数の項目を選択できます。CrtlキーやShiftキーを利用してWindowsのエクスプローラの一覧表示と同じ操作で複数の項目を選択できます。
列挙体の値 MultiSimple 1度に複数の項目を選択できます。選択の動作を繰り返すことで選択を解除できます。
列挙体の値 None 全く項目を選択できません。
列挙体の値 One 1度に1つだけ項目を選択できます。

■表5:SelectionModeプロパティ

MultiExtendedMultiSimpleはどちらも複数選択を可能にしますが選択と選択解除の操作方法が異なります。操作方法の違いは文章で読むよりも実際に試してみた方がよくわかります。プログラムで制御する場合は違いはありません。

 

5−2.選択の制御

プログラムでは以下のメソッド・プロパティを使用して選択されている項目を取得したり、選択状態を制御することができます。

メソッド・プロパティ 説明
プロパティ SelectedIndex 選択されている1つの項目のインデックスを示します。
プロパティ SelectedIndices 選択されている全ての項目のインデックスを示します。
プロパティ SelectedItem 選択されている1つの項目を示します。
プロパティ SelectedItems 選択されている全ての項目のインデックスを示します。
プロパティ Text 選択されている1つの項目の文字列を示します。
メソッド ClearSelected 全ての項目の選択を解除します。
メソッド GetSelected 項目の選択状態を調べます。
メソッド SetSelected 項目の選択状態を設定します。

■表6

この他にデータ連結を使用している場合はSelectedValueプロパティを使用することがあります。

 

5−3.選択されている文字列の取得

選択されている文字列自体を単純に取得したいのであればTextプロパティを使用するのが最も簡単です。

次の例では現在選択されている項目の文字列を取得します。何も選択されていない場合は通常は空文字を返します。複数の項目が選択されている場合はその中で一番先頭の方にある項目の文字列を取得します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Value As String

Value = ListBox1.Text

MsgBox(Value)

■リスト18

Textプロパティには値を設定することもできます。Textプロパティに値を設定した場合、その値と合致する最初の項目が選択状態になります。合致する値がない場合は何も起こりません。ただし、Textプロパティに値を設定すると、以降何も選択されていない場合でもTextプロパティはその設定値を返すようになります。こういった動作はわかりにくいのでTextプロパティには値を設定しないことをお勧めします。

複数選択されている場合に選択されているすべての項目の文字列を取得するにはSelectedItemsプロパティとGetItemTextメソッドを組み合わせて使用します。

 

5−4.選択されているインデックスの取得

SelectedIndexプロパティおよび、SelectedIndicesプロパティは選択されている項目の位置を示します。

SelectedIndexプロパティは項目が何も選択されていない場合は-1を返します。また、複数の項目が選択されている場合はその中で一番先頭の方にある項目のインデックスを取得します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.SelectedIndex

MsgBox(Index & "が選択されています。")

■リスト19

SelectedIndexプロパティには値を設定することもできます。

 

SelectedIndicesプロパティは選択されているインデックスのコレクションです。

次の使用例では選択されている項目の数を取得します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Count As Integer

Count = ListBox1.SelectedIndices.Count

MsgBox(Count & "個の項目が選択されています。")

■リスト20

次の例では選択されているすべての項目のインデックスを列挙します。

VB.NET2003対応 VB2005対応

For Each Index As Integer In ListBox1.SelectedIndices
    MsgBox(Index & "が選択されています。")
Next

■リスト21

次の例では選択されているすべての項目の文字列を列挙します。

 

5−5.選択されている項目の取得

SelectedItemプロパティおよびSelectedItemsプロパティは選択されている項目自体を取得します。

次の例では選択されている項目の型名を取得します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Item As Object

Item = ListBox1.SelectedItem

MsgBox(Item.GetType.FullName)

■リスト22

SelectedItemプロパティには値を設定することもできます。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.SelectedItem = Button1
 

■リスト23

設定した値が一覧に追加されていない場合は何も起こりません。

 

次の例では選択されているすべての項目の型名を取得します。

VB.NET2003対応 VB2005対応

For Each Item As Object In ListBox1.SelectedItems
    MsgBox(Item.GetType.FullName)
Next

■リスト24

 

次の例では選択されているすべての項目の表示されている文字列を取得します。

VB.NET2003対応 VB2005対応

For Each Item As Object In ListBox1.SelectedItems

    Dim
Value As
String

   
Value = ListBox1.GetItemText(Item)
    MsgBox(Value)

Next

■リスト25

 

5−6.選択状態の制御

メソッドを使用して選択の状態自体を変更することができます。

GetSelectedメソッドは指定したインデックスの項目が選択されているか調べます。

次の例では2番目の項目が選択されているかいないかを調べます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

If ListBox1.GetSelected(1) Then
    MsgBox("選択されています。")
Else
   
MsgBox("選択されていません。")
End If

■リスト26

SetSelectedメソッドでは指定したインデックスの項目を選択したり選択を解除したりできます。

次の例では2番目の項目を選択します。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.SetSelected(1, True)
 

■リスト27

次の例では2番目の項目の選択を解除します。もともと選択されていない場合は何も起こりません。

VB.NET2002対応 VB.NET2003対応 VB2005対応


ListBox1.SetSelected(1, False)
 

■リスト28

ItemsプロパティとSetSelectedメソッドを組み合わせることですべての項目を選択することができます。

VB.NET2003対応 VB2005対応

For Index As Integer = 0 To ListBox1.Items.Count - 1
    ListBox1.SetSelected(Index, True)
Next

■リスト29

すべての項目の選択を解除するのも上記のようなループを利用して行えますが、ClearSelectedメソッドを使用するのが最も簡単です。

 

5−7.選択によって発生するイベント

項目の選択状態が変化すると以下のイベントが発生します。

イベント 説明
イベント SelectedIndexChanged 選択されているインデックスを変更するような操作をした場合に発生します。
イベント SelectedValueChanged 選択されている項目を変更するような操作をした場合に発生します。

■表7

これらのイベントはまずSelectedValueChangedイベントが発生してからSelectedIndexChangedイベントが発生します。

ユーザーの操作による選択状態の変化だけではなくプログラムでの制御の結果としてもこれらのイベントが発生します。

また、実際には選択状態の変化が起こらなくても選択状態を変化させるようなアクションを行った場合にはこれらのイベントが発生します。たとえば、同じ項目を2回クリックすると2回目のクリックでは通常は選択状態は変化しませんがイベントは発生します。

 

 

 

6.項目の取得・検索

 

6−1.項目の取得・検索方法

一覧から項目を取得・検索する主な方法を表にまとめます。

メソッド・プロパティ 説明
プロパティ Items すべての項目を取得・設定します。
プロパティ Items.Contains 値が一覧に含まれているか調べます。
メソッド Items.IndexOf 指定した項目のインデックスを取得します。
メソッド FindString 指定した文字列から始まる項目のインデックスを取得します。
メソッド FindStringExact 指定した文字列に合致する項目のインデックスを取得します。
メソッド IndexFromPoint 指定した座標にある項目のインデックスを取得します。

■表8

この他に現在選択されている項目を取得する方法もありますが、それは既に説明しているのでここでは繰り返しません。

 

6−2.すべての項目の取得 - Itemsプロパティ

Itemsプロパティはすべての項目を含むコレクションです。

次の例は3番目の項目を取得します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Item As Object

Item = ListBox1.Items(2)

MsgBox(Item.ToString)

■リスト30

またItemsプロパティはListBox.ObjectCollectionクラスのインスタンスであり、これ自体に様々なメソッドやプロパティが用意されています。

 

6−3.指定した値が一覧に含まれているか - Items.Containsメソッド

Items.Containsメソッドを使用すると指定した値が一覧に含まれているか調べることができます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

If ListBox1.Items.Contains("いぬ") Then
    MsgBox("いぬは一覧にあります。")
Else
   
MsgBox("いぬはありません。")
End If

■リスト31

 

6−4.項目・文字列の検索

あるかないか調べるだけでなく、位置を知りたい場合はItems.IndexOfメソッド使用します。Items.IndexOfメソッドは指定した項目のインデックスを取得することができます。IndexOfメソッドは対象の項目が存在しない場合には-1を返します。複数の項目が該当する場合は先頭に近い方の項目のインデックスを返します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.Items.IndexOf("うま")

If
Index = -1
Then
    MsgBox("うまはありません。")
Else
   
MsgBox("うまは" & Index & "番目です。")
End If

■リスト32

 

この例と同じことをFindStringExactメソッドを使って行うこともできます。FindStringExactメソッドの場合は対象が存在しない場合にListBox.NoMatchesを返しますが、この定数の値は-1なのでIndexOfを使用する例と互換性があります。複数の項目が該当する場合は先頭に近い方の項目のインデックスを返します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.FindStringExact("うま")

If
Index = ListBox.NoMatches
Then
   
MsgBox("うまはありません。")
Else
    MsgBox("うまは" & Index & "番目です。")
End If

■リスト33

IndexOfメソッドとFindStringExactメソッドには2つ違いがあります。

  Items.IndexOf FindStringExact
検索対象 項目 表示されている文字列
検索開始位置指定 不可 可能

■表9

IndexOfメソッドは項目を検索するので文字列以外の項目をListBoxに追加しているときに意味があります。

たとえば、次のように項目を追加したときのことを考えます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.DisplayMember = "Text"
ListBox1.Items.Add(Me)
ListBox1.Items.Add(Button1)
ListBox1.Items.Add(Now)

■リスト34

このとき、次の例ではButton1の位置を取得できます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.Items.IndexOf(Button1)

If
Index = -1
Then
   
MsgBox("Button1はありません。")
Else
    MsgBox("Button1は" & Index & "番目です。")
End If

■リスト35

次の例ではButton1の位置を取得できません。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.Items.IndexOf("Button1")

If
Index = -1
Then
   
MsgBox("Button1はありません。")
Else
    MsgBox("Button1は" & Index & "番目です。")
End If

■リスト36

上の例ではButton1自体を検索しているのに対し、下の例では「Button1」という文字を検索しているからです。たしかにListBoxには「Button1」という文字が表示されていますが、Items.Addメソッドで追加したのは「Button1」という文字ではなくButton1自体なので、下の例では該当が見つかりません。

一方、FindStringExactメソッドではあくまでも表示されている文字を検索します。どのような項目が追加されたのかは問題にしません。ですから次の例では「Button1」の位置を取得できます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.FindStringExact("Button1")

If
Index = ListBox.NoMatches
Then
   
MsgBox("Button1はありません。")
Else
    MsgBox("Button1は" & Index & "番目です。")
End If

■リスト37

 

FindStringExactメソッドには第2引数に検索開始位置を指定することもできます。

次の例ではすべての「うま」という項目の位置を列挙します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer = -1

Do
While Index < ListBox1.Items.Count - 1

    Index = ListBox1.FindStringExact("うま", Index + 1)

    If
Index = ListBox.NoMatches
Then
       
Exit Do
   
End If

   
MsgBox(Index & "番目にうまがいます。")

Loop

■リスト38

 

FindStringメソッドの動作はFindStringExactメソッドの動作とほぼ同じですが、文字列を検索するときに先頭部分のみで比較します。つまり「あ」から始まる文字列や、「ex」からはじまる文字列のように「○○から始まる文字列」を検索できます。

次の例では、「い」から始まる項目を検索します。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Index As Integer

Index = ListBox1.FindString("い")

If
Index = ListBox.NoMatches
Then
    MsgBox("「い」から始まる項目はありません。")
Else
   
MsgBox(Index & "番目に「い」から始まる項目があります。")
End If

■リスト39

 

6−5.指定した座標にあるインデックスの取得

IndexFromPointメソッドは座標から項目のインデックスを取得します。マウス操作などでビジュアルで動きのあるユーザーインターフェースを実現する場合に使用します。

次の例はマウスポイントが近くに来た項目の文字列が変化します。実行したらButton1をクリックしてからマウスをListBoxの各項目に近づけてみてください。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    ListBox1.Items.Add("あさがお")
    ListBox1.Items.Add("いぬ")
    ListBox1.Items.Add("うま")
    ListBox1.Items.Add("えほん")
    ListBox1.Items.Add("おたまじゃくし")

End
Sub

Private Sub ListBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseMove

    Dim
Index As
Integer

   
Index = ListBox1.IndexFromPoint(e.Location)

    SetDefaultAllText()

    If
Index = ListBox.NoMatches
Then
       
Return
   
End If

   
ListBox1.Items(Index) = "■" & ListBox1.Items(Index) & "■"

End Sub

Private Sub SetDefaultAllText()

    For
Index As Integer = 0 To ListBox1.Items.Count - 1
        ListBox1.Items(Index) = Replace(ListBox1.Items(Index), "■", "")
   
Next

End Sub

■リスト40

 

7.オーナー描画

 

7−1.オーナー描画の概要

オーナー描画はオーナードローとも呼ばれます。通常ListBoxの描画はシステムにより自動的に行われますが、これをあえて自分で行うことができます。これがオーナー描画です。

オーナー描画は自分で描画を実行するので通常のListBoxではできないようないろいろな視覚効果を実現することができます。

■画像6:オーナー描画の例。4文字以上の項目を赤い色で表示

ListBoxはオーナー描画を行えるようにイベントやプロパティを通じてさまざまな情報を提供します。

オーナー描画を行うにはDrawModeプロパティをNormal以外の値に設定します。

意味
列挙体の値 Normal オーナー描画を行わない。
列挙体の値 OwnerDrawFixed オーナー描画を行う。ただし、各項目の高さは常に一定である。
列挙体の値 OwnerDrawVariable オーナー描画を行う。項目によって高さが異なる場合がある。

■表10:DrawModeプロパティ

 

7−2.項目の高さを固定したオーナー描画

DrawModeプロパティをOwnerDrawFixedか、OwnerDrawVariableに設定した場合、実際のオーナー描画のプログラムはDrawItemイベントで行います。OwnerDrawVariableを指定した場合にはさらにMeasureItemイベントで項目の大きさを指定します。MeasureItemイベントはDrawItemイベントの直前に発生します。

次の例ではListBox上で4文字以上の文字で表示される項目を赤い色にします。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem

    If
e.Index = -1
Then
       
Return
   
End If

   
'背景を描画する
   
e.DrawBackground()

   
'表示すべき文字列を取得
   
Dim Value As String

   
Value = ListBox1.GetItemText(ListBox1.Items(e.Index))

    Dim
myBrush As Brush

   
'4文字以上の項目だけ赤で描画する。
   
If Len(Value) > 3 Then
       
myBrush = Brushes.Red
   
Else
       
myBrush = New SolidBrush(e.ForeColor)
    End
If

   
'文字の描画
   
e.Graphics.DrawString(Value, e.Font, myBrush, New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
    e.DrawFocusRectangle()

End
Sub

■リスト41

DrawItemイベントは項目ごとに発生します。ListBoxを1回描画するのにDrawItemイベントは複数回発生することになります。ですからこのイベントの中では1つの項目の描画だけに専念すればよく、他の項目の描画は気にする必要はありません。

オーナー描画はすべてを自分で描画するため、さまざまな配慮を行う必要があります。たとえば、選択されている場合の反転表示や、フォーカスがある場合の点線の表示などもすべて行う必要があります。または意図的に行わないこともできます。

DrawItemイベントの引数DrawItemEventArgsクラスはオーナー描画をサポートするさまざまな機能を持っていて、現在描画対象の項目に対する様々な情報を提供してくれたり、標準的な描画を助けてくれたりします。たとえば、上記の例でもあるようにDrawBackgroundメソッドを呼び出すだけで背景を描画してくれますし、DrawFocusRectangleメソッドを呼び出すだけでフォーカスがある時の点線も描画してくれます。

後はこのクラスのGraphicsプロパティを通じて自分の書きたいことを付け足していくというスタンスになります。

 

7−3.項目の高さを可変にしたオーナー描画

次の例では表示する文字列が「うま」である項目だけ大きなフォントで表示します。

■画像7:オーナー描画の例。「うま」だけ目立たせる。

フォントの大きさが変わるので項目によって高さが一定でないことになります。ですからDrawModeにはOwnerDrawVariableを指定して、MeasureItemイベントで高さを指定する必要があります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub ListBox1_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) Handles ListBox1.MeasureItem

    Dim
Value As
String

   
Value = ListBox1.GetItemText(ListBox1.Items(e.Index))

    If
Value = "うま"
Then
       
e.ItemHeight = 30
    End
If

End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem

    If
e.Index = -1
Then
       
Return
   
End If

   
'背景を描画する
   
e.DrawBackground()

   
'表示すべき文字列を取得
   
Dim Value As String
   
Value = ListBox1.GetItemText(ListBox1.Items(e.Index))

   
'「うま」だけフォントをポイントにして青くする。
   
Dim myBrush As Brush
    Dim MyFont As Font

    If
Value = "うま"
Then
       
myBrush = Brushes.Blue
        MyFont = New Font(e.Font.FontFamily, 20)
   
Else
       
myBrush = New SolidBrush(e.ForeColor)
        MyFont = e.Font
    End
If

   
'文字の描画
   
e.Graphics.DrawString(Value, MyFont, myBrush, New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
    e.DrawFocusRectangle()

End
Sub

■リスト42

 

8.データ連結

 

ListBoxは他のコントロールと同様の方法でデータ連結を設定できます。

データ連結についてはデータベース講座第5回 データの一覧表示を参考にしてください。

データ連結を行うにはDataSourceプロパティを使用しますが、DataSourceプロパティを使用した場合はItemsプロパティを使った項目の変更ができなくなるので注意してください。Items.Countプロパティなど項目を変更しない機能は使用できます。

 

データ連結に使用するDataSourceプロパティにはIListインターフェースまたはIListSourceインターフェースを実装するオブジェクトを指定できます。

ですからDataTableArrayListGeneric.Listなどのクラスをセットすることができます。このときDisplayMemberプロパティを設定してListBoxに表示する文字列を取得方法を指定することができます。

DisplayMemberプロパティにはプロパティの名前を設定します。項目を文字列に変換するときにここで設定したプロパティが使用されます。DisplayMemberプロパティに何も設定されていない場合や、DisplayMemberに設定されているプロパティが項目に存在しない場合にはListBoxToStringメソッドを呼び出して文字列変換を行います。

ValueMemberプロパティはListBoxSelectedValueプロパティで取得する内容を設定します。ValueMemberプロパティにもプロパティの名前を設定し、対象の項目が選択されている場合にSelectedValueプロパティで値を読み取ったときにはこのプロパティの値を取得します。

この関係を模式的にまとめると次の図のようになります。

■図2

取得できる値はValueMemberの設定によって異なるので図では?にしてあります。

以下はDisplayMemberプロパティとSelectedValueプロパティの使用例です。ListBoxには日本、韓国、台湾と漢字で国の名前が表示されますが、Button2をクリックしてSelectedValueを取得したときにはひらがなで取得できます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Dim
Ar As New ArrayList

    Ar.Add(New DictionaryEntry("日本", "にほん"))
    Ar.Add(New DictionaryEntry("韓国", "かんこく"))
    Ar.Add(New DictionaryEntry("台湾", "たいわん"))

    ListBox1.DisplayMember =
"Key"
   
ListBox1.ValueMember = "Value"

   
ListBox1.DataSource = Ar

End
Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    MsgBox(ListBox1.SelectedValue)

End
Sub

■リスト43

 

 

9.その他の機能

 

9−1.ソート

Sortedプロパティを使用すると項目を自動的に辞書順に並び替えることができます。項目のインデックス常に並び順に一致します。

ListBoxに限った話ではありませんが、項目を辞書順に並べるときには数字の並び順に注意してください。

次の例では項目は123, 1234, 9の順で並びます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

ListBox1.Items.Add(123)
ListBox1.Items.Add(9)
ListBox1.Items.Add(1234)

ListBox1.Sorted = True

■リスト44

この例は数値としてみれば9が一番小さいので9が先頭に来るべきかとも思えますが、並び替えは辞書順に行われるので9が一番最後になります。つまり、「11」と「9」では「9」の方が大きいということになるのです。これは「ああ」と「お」では「お」の方が後にくるというのと同じことです。

 

並び替えの結果が気に入らなければ自分で独自の並び順を定義することもできます。独自の並び順を指定する具体的な例はサンプルの項目を並び替えるを参照してください。

 

9−2.複数列

MultiColumnプロパティを使用すると複数列で項目を表示することができます。ただし、ここで言う複数列とは表のようなイメージではなく、縦に並びきれない項目が折り返して表示されるというだけのことです。

■画像8:複数列表示

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim Items() As String = {"北海道", "青森", "秋田", "岩手", "山形", "宮城", "福島", "群馬", "栃木", "茨城", "埼玉", "千葉", "東京", "神奈川"}

ListBox1.Items.AddRange(Items)
ListBox1.ColumnWidth = 50
ListBox1.MultiColumn = True

■リスト45

複数列表示を行う場合はColumnWidthプロパティで列の幅を設定できます。

 

9−3.書式設定

VB2005以降ではFormatStringプロパティを使用して表示する文字列の書式を設定することができます。

次の例では数値が8ケタになるように0で埋めて表示します。

■画像9:FormatStringプロパティの設定

VB2005対応

ListBox1.Items.Add(123)
ListBox1.Items.Add(9)
ListBox1.Items.Add(1234)

ListBox1.FormatString = "00000000"

■リスト46

FormattingEnabledプロパティを使用すると書式設定の有効・無効が簡単に設定できます。初期値はTrueです。

 

9−4.VB6のItemDataプロパティと同じような動作

最後にVB6を使用していプログラマ向けに情報を提供します。

VB6のListBoxにはItemDataというプロパティがあり、表示している一覧とは別に表示されない一覧を持つことができました。この2つの一覧はたとえば、学籍番号と学生の名前のように対になっている値を管理するのに便利でした。ただし、ItemDataの一覧には数値項目しか設定できないのが難点でした。

VB.NET2002以降では一覧はあくまでもItemsプロパティの一覧だけになりましたが、文字列以外の値も設定できるようになったためVB6のItemDataプロパティよりも柔軟に対になっている値や連動する値を管理することができます。

以下のVB6のItemDataプロパティの使用例を示します。

VB6対応

Private Sub Command1_Click()

    List1.AddItem "徳川家康"
    List1.ItemData(0) = 100

    List1.AddItem "豊臣秀吉"
    List1.ItemData(1) = 221

    List1.AddItem "織田信長"
    List1.ItemData(2) = 395

End Sub
Private Sub List1_Click()

    Dim
Text As String
    Dim
Value As String

   
Text = List1.List(List1.ListIndex)
    Value = List1.ItemData(List1.ListIndex)

    MsgBox Text & "は" & Value & "番です。"

End Sub

■リスト47

このプログラムはVB.NET2002以降では次のプログラムと同じ意味です。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    ListBox1.DisplayMember =
"Key"

   
ListBox1.Items.Add(New DictionaryEntry("徳川家康", 100))
    ListBox1.Items.Add(New DictionaryEntry("豊臣秀吉", 221))
    ListBox1.Items.Add(New DictionaryEntry("織田信長", 395))

End
Sub

Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

    Dim
Text As
String
   
Dim Value As String

    Text = CType(ListBox1.SelectedItem, DictionaryEntry).Key
    Value = CType(ListBox1.SelectedItem, DictionaryEntry).Value
 
    MsgBox(Text & "は" & Value & "番です。")

End
Sub

■リスト48

ここでは簡単に書くためにDictionaryEntry構造体使用しましたが、独自のクラスを使用することもできます。また、KeyValuePair構造体を使用してより厳密に型指定することもできます。

また、この例はVB6のItemDataとの比較がわかりやすくなるように書いていますが、DataSourceプロパティとValueMemberプロパティを使用してもう少し簡単に書くこともできます。