Visual Basic クラスライブラリ詳解 |
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以上) |
主な機能 | 文字列の一覧を表示する。 |
ListBoxクラスは文字による一覧を表示するためのコントロールです。通常はFormなどのコンテナに貼り付けて使用します。コントロールであるので、ListBoxクラスというよりもListBoxコントロールと呼ばれることが多いです。
■画像1:ListBox
ListBoxは一覧を表示するための最もシンプルなコントロールです。基本的には1列しか表示できません。表が必要な場合にはListBoxではなくDataGridViewなどの使用を検討します。 その他のListBoxに似たコントロールについては下の囲み記事を参照してください。
ユーザーはListBoxに表示された1つまたは複数の項目を選択することができます。しかし、直接入力して値を変更したり削除することはできません。プログラムで制御する場合は値の変更や削除は簡単に行えます。
上述のようにListBoxはシンプルな一覧表示を行うコントロールです。ここではListBoxに似た機能を持つコントロールを紹介しますのでListBoxでは難しい仕様を実現しようとしている場合に検討してみてください。
■表1:ListBoxに似たコントロール |
ListBoxの一覧は通常Itemsプロパティによって制御されます。一覧の項目を編集するにはデザイン時にプロパティウィンドウを使用するか、プログラムでItemsプロパティにアクセスします。
プロパティウィンドウでは文字列コレクションエディタを使用してItemsプロパティを編集することができます。
■画像2:文字列コレクションエディタ
このエディタ上の1行が一覧の項目の1つになります。
ListBoxのスマートタグを使用して一覧を編集することもできます。ListBoxのスマートタグを表示するにはListBoxを選択した状態で右上に表示される小さな三角形のボタンをクリックして、「項目の編集」を選びます。
■画像3:ListBoxのタスク
この場合でもプロパティウィンドウと同様に文字列コレクションエディタが表示されます。
プログラムで制御する場合は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つの項目を追加します。
ListBox1.Items.Add("あさがお") ListBox1.Items.Add("いぬ") ListBox1.Items.Add("うま") |
■リスト1
次の例はAddメソッドを使用して3つの項目を追加した後で、Insertメソッドを使用して2番目に項目を挿入します。
ListBox1.Items.Add("あさがお") ListBox1.Items.Add("いぬ") ListBox1.Items.Add("うま") ListBox1.Items.Insert(1, "★挿入★") |
■リスト2
項目の位置ははじめの項目が0なので、2番目に挿入するには1を指定します。
Insertメソッドのようにインデックスを指定するメソッドやプロパティでは存在しないインデックスを指定すると例外ArgumentOutOfRangeExceptioinが発生するので常に注意してください。
次の例は1000番目が存在しないのでエラーになります。
ListBox1.Items.Add("あさがお") ListBox1.Items.Add("いぬ") ListBox1.Items.Add("うま") ListBox1.Items.Insert(999, "★挿入★") '←ここでエラー |
■リスト3
次の例はAddRangeメソッドを使用してListBox1に3つの項目を追加します。
ListBox1.Items.AddRange(New String() {"あさがお", "いぬ", "うま"}) |
■リスト4
AddRangeメソッドは配列かListBox.ObjectCollectionクラスを引数にとります。配列を使用した次のようなプログラムも有効です。
Dim Values(2) As
String Values(0) = "あさがお" Values(1) = "いぬ" Values(2) = "うま" ListBox1.Items.AddRange(Values) |
■リスト5
AddメソッドやInsertメソッドを使用して大量の項目を追加するとかなり処理に時間がかかる場合があります。これは項目を1つ追加するたびに再描画が行われるためです。一度に大量の項目を追加する場合はすべての項目の追加が完了した時点で1回だけ再描画を行えばよいことがほとんどなのでその都度再描画するのは非効率です。
BeginUpdateメソッドを使用すると再描画のタイミングを制御できるので、1つずつ項目を追加したときの処理速度の問題を解決できます。BeginUpdateメソッドを使用するとEndUpdateメソッドが呼び出されるまで再描画を行いません。
次の例はBeginUpdateメソッドを使用しないで大量の項目を追加します。
Dim StartTime As
Double = DateAndTime.Timer For i As Integer = 0 To 10000 ListBox1.Items.Add(i) Next MsgBox(DateAndTime.Timer - StartTime & "秒かかりました。") |
■リスト6
処理の開始前に時間を記録しておくようにしてあるので、この処理にかかる時間がわかります。
処理の前後でBeginUpdateとEndUpdateを使用すると圧倒的に速度が向上します。
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メソッドを使用する必要はありません。
全ての項目の個数を調べるにはItems.Countプロパティを使用します。このプロパティはごく簡単に使用できます。
Dim Count As
Integer Count = ListBox1.Items.Count MsgBox(Count & "個の項目があります。") |
■リスト8
ListBoxの一覧の個々の要素は通常は文字列を指定しますが、実際にはあらゆる種類のオブジェクトを一覧に追加することができます。 ただし文字列以外のものを追加すると処理が複雑になるので通常はお勧めできません。
次の例は有効です。
ListBox1.Items.Add(Me) ListBox1.Items.Add(Button1) ListBox1.Items.Add(Now) |
■リスト9
この例を実行するとListBox1には次のように表示されます。
■画像4:文字列以外の物をItemに追加
ListBoxはどのような種類のオブジェクトであれ文字列に変換して表示しようとします。文字列に変換するためにそれぞれのオブジェクトのToStringメソッドを呼び出すのでこのような結果になります。
Me.ToStringおよびButton1.ToStringは何も返 さないので、表示は空欄になります。Now.ToStringは現在の日付を返すのでこの分だけ表示されます。
DisplayMemberプロパティを使用すると文字列に変換するのではなく、指定したプロパティの値を表示させることができます。たとえば、それぞれのオブジェクトのTextプロパティの値を一覧に表示させるにはDisplayMemberに"Text"をセットします。
ListBox1.DisplayMember = "Text" ListBox1.Items.Add(Me) ListBox1.Items.Add(Button1) ListBox1.Items.Add(Now) |
■リスト10
この例ではListBox1の表示は次のようになります。
■画像5:DisplayMember設定後
この例ではNowにはDisplayMemberで設定したTextプロパティが存在 しません。この場合には文字列変換にToStringメソッドが使用されます。
オブジェクトを一覧に追加した場合、表示上はすべて文字列になりますが一覧の内容としてはそのオブジェクトを保持し続けます。
たとえば、一覧にButton1を追加した後で、ItemsプロパティからButton1を取得することも可能です。
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には常に文字列しか追加しないというのであれば特に区別する必要はありません。
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.AddやItems.InsertなどItemsのメンバはすべて表示ではなく項目に関するものということになります。
ただし、若干の例外があるのでこの図がすべてではありません。
表示欄ではダブルクォーテーション付きで「"Form1"」となっているのが項目欄ではダブルクォーテーションなしで「Form1」となっている点も注意してください。
また、インデックス・表示・項目の相互の関係については矢印で示してあります。たとえば、項目から表示を取得するにはGetItemTextメソッドを使用することがわかります。DisplayMemberプロパティも項目から表示を取得しますが、こちらはシステムが内部で使用する設定であってプログラム中直接DisplayMemberを使用して表示を取得することはできません。こういった細かいことはこの図では表現されていません。
あとあと表示や項目の関係について混乱してきたらこの図を見直してみてください。
なお、ListBoxに文字列型の値した追加しない場合は、表示されている文字列と項目は常に一致しているので単純に考えることができます。ですから、特に理由がない限りListBoxには文字列だけを追加することをお勧めします。
項目を削除に使用する主なメソッドは次の通りです。
メソッド・プロパティ | 説明 |
Items.Clear | 全ての項目の削除します。 |
Items.Remove | 指定した項目を削除します。 |
Items.RemoveAt | 指定した位置にある項目を削除します。 |
■表4
Clearメソッドはすべての項目を削除してListBoxをまっさらな状態にします。
ListBox1.Items.Clear() |
■リスト13
Removeメソッドは指定した項目を削除します。全ての項目が文字列である場合は単純にその文字列を指定して項目を削除することができます。該当項目が複数ある場合は一番先頭に近い項目だけを削除します。
次の例では「いぬ」という文字列の項目を削除します。
ListBox1.Items.Remove("いぬ") |
■リスト14
次の例はButton1である項目を削除します。
ListBox1.Items.Remove(Button1) |
■リスト15
対の例では「Button1」という文字列の項目を削除します。
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番目の項目を削除します。
ListBox1.Items.RemoveAt(1) |
■リスト17
ListBox内の項目はマウスおよびキーボードの操作で選択することができます。初期状態では1度に選択可能な項目は1つだけですが、SelectionModeプロパティを設定することで1度に複数の項目を選択できるようにできますし、逆に全く項目を選択できないようにもできます。
SelectionModeプロパティに設定可能な値とその意味は次の通りです。
値 | 機能 |
MultiExtended | 1度に複数の項目を選択できます。CrtlキーやShiftキーを利用してWindowsのエクスプローラの一覧表示と同じ操作で複数の項目を選択できます。 |
MultiSimple | 1度に複数の項目を選択できます。選択の動作を繰り返すことで選択を解除できます。 |
None | 全く項目を選択できません。 |
One | 1度に1つだけ項目を選択できます。 |
■表5:SelectionModeプロパティ
MultiExtendedとMultiSimpleはどちらも複数選択を可能にしますが選択と選択解除の操作方法が異なります。操作方法の違いは文章で読むよりも実際に試してみた方がよくわかります。プログラムで制御する場合は違いはありません。
プログラムでは以下のメソッド・プロパティを使用して選択されている項目を取得したり、選択状態を制御することができます。
メソッド・プロパティ | 説明 |
SelectedIndex | 選択されている1つの項目のインデックスを示します。 |
SelectedIndices | 選択されている全ての項目のインデックスを示します。 |
SelectedItem | 選択されている1つの項目を示します。 |
SelectedItems | 選択されている全ての項目のインデックスを示します。 |
Text | 選択されている1つの項目の文字列を示します。 |
ClearSelected | 全ての項目の選択を解除します。 |
GetSelected | 項目の選択状態を調べます。 |
SetSelected | 項目の選択状態を設定します。 |
■表6
この他にデータ連結を使用している場合はSelectedValueプロパティを使用することがあります。
選択されている文字列自体を単純に取得したいのであればTextプロパティを使用するのが最も簡単です。
次の例では現在選択されている項目の文字列を取得します。何も選択されていない場合は通常は空文字を返します。複数の項目が選択されている場合はその中で一番先頭の方にある項目の文字列を取得します。
Dim Value As
String Value = ListBox1.Text MsgBox(Value) |
■リスト18
Textプロパティには値を設定することもできます。Textプロパティに値を設定した場合、その値と合致する最初の項目が選択状態になります。合致する値がない場合は何も起こりません。ただし、Textプロパティに値を設定すると、以降何も選択されていない場合でもTextプロパティはその設定値を返すようになります。こういった動作はわかりにくいのでTextプロパティには値を設定しないことをお勧めします。
複数選択されている場合に選択されているすべての項目の文字列を取得するにはSelectedItemsプロパティとGetItemTextメソッドを組み合わせて使用します。
SelectedIndexプロパティおよび、SelectedIndicesプロパティは選択されている項目の位置を示します。
SelectedIndexプロパティは項目が何も選択されていない場合は-1を返します。また、複数の項目が選択されている場合はその中で一番先頭の方にある項目のインデックスを取得します。
Dim Index As
Integer Index = ListBox1.SelectedIndex MsgBox(Index & "が選択されています。") |
■リスト19
SelectedIndexプロパティには値を設定することもできます。
SelectedIndicesプロパティは選択されているインデックスのコレクションです。
次の使用例では選択されている項目の数を取得します。
Dim Count As
Integer Count = ListBox1.SelectedIndices.Count MsgBox(Count & "個の項目が選択されています。") |
■リスト20
次の例では選択されているすべての項目のインデックスを列挙します。
For Each
Index As Integer
In ListBox1.SelectedIndices MsgBox(Index & "が選択されています。") Next |
■リスト21
次の例では選択されているすべての項目の文字列を列挙します。
SelectedItemプロパティおよびSelectedItemsプロパティは選択されている項目自体を取得します。
次の例では選択されている項目の型名を取得します。
Dim Item As
Object Item = ListBox1.SelectedItem MsgBox(Item.GetType.FullName) |
■リスト22
SelectedItemプロパティには値を設定することもできます。
ListBox1.SelectedItem = Button1 |
■リスト23
設定した値が一覧に追加されていない場合は何も起こりません。
次の例では選択されているすべての項目の型名を取得します。
For Each
Item As Object
In ListBox1.SelectedItems MsgBox(Item.GetType.FullName) Next |
■リスト24
次の例では選択されているすべての項目の表示されている文字列を取得します。
For Each
Item As Object
In ListBox1.SelectedItems Dim Value As String Value = ListBox1.GetItemText(Item) MsgBox(Value) Next |
■リスト25
メソッドを使用して選択の状態自体を変更することができます。
GetSelectedメソッドは指定したインデックスの項目が選択されているか調べます。
次の例では2番目の項目が選択されているかいないかを調べます。
If ListBox1.GetSelected(1)
Then MsgBox("選択されています。") Else MsgBox("選択されていません。") End If |
■リスト26
SetSelectedメソッドでは指定したインデックスの項目を選択したり選択を解除したりできます。
次の例では2番目の項目を選択します。
ListBox1.SetSelected(1, True) |
■リスト27
次の例では2番目の項目の選択を解除します。もともと選択されていない場合は何も起こりません。
ListBox1.SetSelected(1, False) |
■リスト28
ItemsプロパティとSetSelectedメソッドを組み合わせることですべての項目を選択することができます。
For Index As
Integer = 0 To
ListBox1.Items.Count - 1 ListBox1.SetSelected(Index, True) Next |
■リスト29
すべての項目の選択を解除するのも上記のようなループを利用して行えますが、ClearSelectedメソッドを使用するのが最も簡単です。
項目の選択状態が変化すると以下のイベントが発生します。
イベント | 説明 |
SelectedIndexChanged | 選択されているインデックスを変更するような操作をした場合に発生します。 |
SelectedValueChanged | 選択されている項目を変更するような操作をした場合に発生します。 |
■表7
これらのイベントはまずSelectedValueChangedイベントが発生してからSelectedIndexChangedイベントが発生します。
ユーザーの操作による選択状態の変化だけではなくプログラムでの制御の結果としてもこれらのイベントが発生します。
また、実際には選択状態の変化が起こらなくても選択状態を変化させるようなアクションを行った場合にはこれらのイベントが発生します。たとえば、同じ項目を2回クリックすると2回目のクリックでは通常は選択状態は変化しませんがイベントは発生します。
一覧から項目を取得・検索する主な方法を表にまとめます。
メソッド・プロパティ | 説明 |
Items | すべての項目を取得・設定します。 |
Items.Contains | 値が一覧に含まれているか調べます。 |
Items.IndexOf | 指定した項目のインデックスを取得します。 |
FindString | 指定した文字列から始まる項目のインデックスを取得します。 |
FindStringExact | 指定した文字列に合致する項目のインデックスを取得します。 |
IndexFromPoint | 指定した座標にある項目のインデックスを取得します。 |
■表8
この他に現在選択されている項目を取得する方法もありますが、それは既に説明しているのでここでは繰り返しません。
Itemsプロパティはすべての項目を含むコレクションです。
次の例は3番目の項目を取得します。
Dim Item As
Object Item = ListBox1.Items(2) MsgBox(Item.ToString) |
■リスト30
またItemsプロパティはListBox.ObjectCollectionクラスのインスタンスであり、これ自体に様々なメソッドやプロパティが用意されています。
Items.Containsメソッドを使用すると指定した値が一覧に含まれているか調べることができます。
If ListBox1.Items.Contains("いぬ")
Then MsgBox("いぬは一覧にあります。") Else MsgBox("いぬはありません。") End If |
■リスト31
あるかないか調べるだけでなく、位置を知りたい場合はItems.IndexOfメソッド使用します。Items.IndexOfメソッドは指定した項目のインデックスを取得することができます。IndexOfメソッドは対象の項目が存在しない場合には-1を返します。複数の項目が該当する場合は先頭に近い方の項目のインデックスを返します。
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を使用する例と互換性があります。複数の項目が該当する場合は先頭に近い方の項目のインデックスを返します。
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に追加しているときに意味があります。
たとえば、次のように項目を追加したときのことを考えます。
ListBox1.DisplayMember = "Text" ListBox1.Items.Add(Me) ListBox1.Items.Add(Button1) ListBox1.Items.Add(Now) |
■リスト34
このとき、次の例ではButton1の位置を取得できます。
Dim Index As
Integer Index = ListBox1.Items.IndexOf(Button1) If Index = -1 Then MsgBox("Button1はありません。") Else MsgBox("Button1は" & Index & "番目です。") End If |
■リスト35
次の例ではButton1の位置を取得できません。
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」の位置を取得できます。
Dim Index As
Integer Index = ListBox1.FindStringExact("Button1") If Index = ListBox.NoMatches Then MsgBox("Button1はありません。") Else MsgBox("Button1は" & Index & "番目です。") End If |
■リスト37
FindStringExactメソッドには第2引数に検索開始位置を指定することもできます。
次の例ではすべての「うま」という項目の位置を列挙します。
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」からはじまる文字列のように「○○から始まる文字列」を検索できます。
次の例では、「い」から始まる項目を検索します。
Dim Index As
Integer Index = ListBox1.FindString("い") If Index = ListBox.NoMatches Then MsgBox("「い」から始まる項目はありません。") Else MsgBox(Index & "番目に「い」から始まる項目があります。") End If |
■リスト39
IndexFromPointメソッドは座標から項目のインデックスを取得します。マウス操作などでビジュアルで動きのあるユーザーインターフェースを実現する場合に使用します。
次の例はマウスポイントが近くに来た項目の文字列が変化します。実行したらButton1をクリックしてからマウスをListBoxの各項目に近づけてみてください。
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 |
Private Sub
SetDefaultAllText() |
■リスト40
オーナー描画はオーナードローとも呼ばれます。通常ListBoxの描画はシステムにより自動的に行われますが、これをあえて自分で行うことができます。これがオーナー描画です。
オーナー描画は自分で描画を実行するので通常のListBoxではできないようないろいろな視覚効果を実現することができます。
■画像6:オーナー描画の例。4文字以上の項目を赤い色で表示
ListBoxはオーナー描画を行えるようにイベントやプロパティを通じてさまざまな情報を提供します。
オーナー描画を行うにはDrawModeプロパティをNormal以外の値に設定します。
値 | 意味 |
Normal | オーナー描画を行わない。 |
OwnerDrawFixed | オーナー描画を行う。ただし、各項目の高さは常に一定である。 |
OwnerDrawVariable | オーナー描画を行う。項目によって高さが異なる場合がある。 |
■表10:DrawModeプロパティ
DrawModeプロパティをOwnerDrawFixedか、OwnerDrawVariableに設定した場合、実際のオーナー描画のプログラムはDrawItemイベントで行います。OwnerDrawVariableを指定した場合にはさらにMeasureItemイベントで項目の大きさを指定します。MeasureItemイベントはDrawItemイベントの直前に発生します。
次の例ではListBox上で4文字以上の文字で表示される項目を赤い色にします。
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:オーナー描画の例。「うま」だけ目立たせる。
フォントの大きさが変わるので項目によって高さが一定でないことになります。ですからDrawModeにはOwnerDrawVariableを指定して、MeasureItemイベントで高さを指定する必要があります。
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 |
■リスト42
ListBoxは他のコントロールと同様の方法でデータ連結を設定できます。
データ連結についてはデータベース講座第5回 データの一覧表示を参考にしてください。
データ連結を行うにはDataSourceプロパティを使用しますが、DataSourceプロパティを使用した場合はItemsプロパティを使った項目の変更ができなくなるので注意してください。Items.Countプロパティなど項目を変更しない機能は使用できます。
データ連結に使用するDataSourceプロパティにはIListインターフェースまたはIListSourceインターフェースを実装するオブジェクトを指定できます。
ですからDataTableやArrayListやGeneric.Listなどのクラスをセットすることができます。このときDisplayMemberプロパティを設定してListBoxに表示する文字列を取得方法を指定することができます。
DisplayMemberプロパティにはプロパティの名前を設定します。項目を文字列に変換するときにここで設定したプロパティが使用されます。DisplayMemberプロパティに何も設定されていない場合や、DisplayMemberに設定されているプロパティが項目に存在しない場合にはListBoxはToStringメソッドを呼び出して文字列変換を行います。
ValueMemberプロパティはListBoxのSelectedValueプロパティで取得する内容を設定します。ValueMemberプロパティにもプロパティの名前を設定し、対象の項目が選択されている場合にSelectedValueプロパティで値を読み取ったときにはこのプロパティの値を取得します。
この関係を模式的にまとめると次の図のようになります。
■図2
取得できる値はValueMemberの設定によって異なるので図では?にしてあります。
以下はDisplayMemberプロパティとSelectedValueプロパティの使用例です。ListBoxには日本、韓国、台湾と漢字で国の名前が表示されますが、Button2をクリックしてSelectedValueを取得したときにはひらがなで取得できます。
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 |
■リスト43
Sortedプロパティを使用すると項目を自動的に辞書順に並び替えることができます。項目のインデックス常に並び順に一致します。
ListBoxに限った話ではありませんが、項目を辞書順に並べるときには数字の並び順に注意してください。
次の例では項目は123, 1234, 9の順で並びます。
ListBox1.Items.Add(123) ListBox1.Items.Add(9) ListBox1.Items.Add(1234) ListBox1.Sorted = True |
■リスト44
この例は数値としてみれば9が一番小さいので9が先頭に来るべきかとも思えますが、並び替えは辞書順に行われるので9が一番最後になります。つまり、「11」と「9」では「9」の方が大きいということになるのです。これは「ああ」と「お」では「お」の方が後にくるというのと同じことです。
並び替えの結果が気に入らなければ自分で独自の並び順を定義することもできます。独自の並び順を指定する具体的な例はサンプルの項目を並び替えるを参照してください。
MultiColumnプロパティを使用すると複数列で項目を表示することができます。ただし、ここで言う複数列とは表のようなイメージではなく、縦に並びきれない項目が折り返して表示されるというだけのことです。
■画像8:複数列表示
Dim Items() As
String = {"北海道",
"青森", "秋田",
"岩手", "山形",
"宮城", "福島",
"群馬", "栃木",
"茨城", "埼玉",
"千葉", "東京",
"神奈川"} ListBox1.Items.AddRange(Items) ListBox1.ColumnWidth = 50 ListBox1.MultiColumn = True |
■リスト45
複数列表示を行う場合はColumnWidthプロパティで列の幅を設定できます。
VB2005以降ではFormatStringプロパティを使用して表示する文字列の書式を設定することができます。
次の例では数値が8ケタになるように0で埋めて表示します。
■画像9:FormatStringプロパティの設定
ListBox1.Items.Add(123) ListBox1.Items.Add(9) ListBox1.Items.Add(1234) ListBox1.FormatString = "00000000" |
■リスト46
FormattingEnabledプロパティを使用すると書式設定の有効・無効が簡単に設定できます。初期値はTrueです。
最後にVB6を使用していプログラマ向けに情報を提供します。
VB6のListBoxにはItemDataというプロパティがあり、表示している一覧とは別に表示されない一覧を持つことができました。この2つの一覧はたとえば、学籍番号と学生の名前のように対になっている値を管理するのに便利でした。ただし、ItemDataの一覧には数値項目しか設定できないのが難点でした。
VB.NET2002以降では一覧はあくまでもItemsプロパティの一覧だけになりましたが、文字列以外の値も設定できるようになったためVB6のItemDataプロパティよりも柔軟に対になっている値や連動する値を管理することができます。
以下のVB6のItemDataプロパティの使用例を示します。
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以降では次のプログラムと同じ意味です。
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 |
■リスト48
ここでは簡単に書くためにDictionaryEntry構造体使用しましたが、独自のクラスを使用することもできます。また、KeyValuePair構造体を使用してより厳密に型指定することもできます。
また、この例はVB6のItemDataとの比較がわかりやすくなるように書いていますが、DataSourceプロパティとValueMemberプロパティを使用してもう少し簡単に書くこともできます。