リストボックスの高度な使い方

注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。

1.選択されているアイテムと、そのインデックスの取得


 .NET Frameworkのクラスライブラリに含まれるSystem.Windows.Forms.ListBoxクラスは、VB6以前のListBoxが機能拡張されたものと言えます。 というのも、多くの動作はそのままで様々な機能が追加されているからです。 ここではそのような追加された機能を見ていきたいと思います。
 まずは、選択されているアイテムと、そのインデックスの取得について見てみます。 リストボックスのアイテムの内、選択されているものはSelectedItemsプロパティ、そのインデックスはSelectedIndicesプロパティによって取得できます。 これらのプロパティはIEnumerableインターフェイスを実装したコレクションになっているので、For Each文によって列挙できます。
 次のサンプルは、インデックスと同じ値を振った文字列アイテムをリストボックスに追加し、選択されているアイテムが変わったときに生じるSelectedValueChangedイベントで選択されているアイテムとそのインデックスを列挙しています。
ソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With listBoxItems

            ' 選択モード
            .SelectionMode = SelectionMode.MultiExtended

            With .Items

                Dim i As Integer

                For i = 0 To 9

                    .Add("Item" + i.ToString())

                Next

            End With

        End With

    End Sub

    Private Sub listBoxItems_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles listBoxItems.SelectedValueChanged

        ' 選択されているアイテム一覧を表示
        Dim o As Object

        listBoxSelectedItems.Items.Clear()

        For Each o In listBoxItems.SelectedItems

            listBoxSelectedItems.Items.Add(o)

        Next

        ' 選択されているアイテムのインデックス一覧を表示
        Dim i As Integer

        listBoxSelectedIndices.Items.Clear()

        For Each i In listBoxItems.SelectedIndices

            listBoxSelectedIndices.Items.Add(i)

        Next

    End Sub

End Class
実行結果
実行結果

2.様々な型のアイテムの追加


 リストボックスには、様々な型のアイテムを追加することもできます。 というのも、アイテムのコレクションはすべての型の基底クラスであるObject型でアイテムを扱っているのためです。 また、文字列以外の型をアイテムとして追加すると、その場合はToString()メソッドによって得られる文字列がリストボックスに表示される文字列となります。
 次のコードで様々な型のアイテムを追加し、どのように表示されるかを検証してみます。
ソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With listBoxItems

            ' 選択モード
            .SelectionMode = SelectionMode.MultiExtended

            ' 様々な型のアイテムを追加
            With .Items

                .Add(123456)
                .Add(3.14159265358)
                .Add("文字列")
                .Add(Color.Red)
                .Add(SortOrder.Ascending)
                .Add(New Point(320, 240))
                .Add(New ArrayList())
                .Add(New Pen(Color.Cyan))

            End With

        End With

    End Sub

    Private Sub listBoxItems_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles listBoxItems.SelectedValueChanged

        ' 選択されているアイテム一覧を表示
        Dim o As Object

        listBoxSelectedItems.Items.Clear()

        For Each o In listBoxItems.SelectedItems

            listBoxSelectedItems.Items.Add(o)

        Next

        ' 選択されているアイテムのインデックス一覧を表示
        Dim i As Integer

        listBoxSelectedIndices.Items.Clear()

        For Each i In listBoxItems.SelectedIndices

            listBoxSelectedIndices.Items.Add(i)

        Next

    End Sub

End Class
実行結果
実行結果

3.独自のアイテム並べ替え


 SortedプロパティをTrueに指定すると、表示されている文字のアルファベット順でアイテムが並べ替えられます。 多くの場合はアルファベット順でも問題ないのですが、文字列型以外の型をアイテムとして格納しているとき、アルファベット順では不適切な場合が生じてきます。 そこで、アルファベット順ではない独自の並べ替えを行ってみたいと思います。 そのためにはArrayListのAdapter()メソッドを利用します。
 このメソッドは、IListインターフェイスに対してそれをラップするArrayListのラッパーを作成します。 つまり、IListが実装していないSort()メソッドをArrayListにラップさせることで利用することができるようになります。 Sort()メソッドでは比較子を何も指定しないとデフォルトでアルファベット順に並べ替えられますが、IComparerを実装したクラスのインスタンスを引数に与えると、独自に定義した並べ替えを行わせることができます。
 次のコードでは、Point型のアイテムをあらかじめリストボックスに追加しておき、その後でArrayListのラッパーを作成して並べ替えを行います。 並べ替え順を定義するクラスはPointComparerで、このクラスではPoint型変数のYプロパティの値によって並べ替えを行います。 参考までに、SortedプロパティをTrueにしたときの実行結果も添えておきます。
ソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
' 独自の並べ替えを定義するクラス
Public Class PointComparer

    Implements IComparer

    ' 座標値による比較
    Public Function Compare(ByVal objX As Object, ByVal objY As Object) As Integer Implements IComparer.Compare

        If objX Is Nothing Then Return 1
        If objY Is Nothing Then Return -1

        Dim ptX As Point = CType(objX, Point)
        Dim ptY As Point = CType(objY, Point)

        If ptX.Y = ptY.Y Then

            ' Yの値が等しいときは、座標は等しいと見なす
            Return 0

        ElseIf ptX.Y > ptY.Y Then

            ' Yの値が大きいときは、座標は大きいと見なす
            Return 1

        Else

            ' それ以外の場合は、座標は小さいと見なす
            Return -1

        End If

    End Function

End Class

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        With listBoxItems

            ' 選択モード
            .SelectionMode = SelectionMode.MultiExtended

            ' Point型のアイテムを追加
            With .Items

                .Add(New Point(320, 240))
                .Add(New Point(173, 141))
                .Add(New Point(640, 480))
                .Add(New Point(320, 200))
                .Add(New Point(141, 173))
                .Add(New Point(480, 640))
                .Add(New Point(320, 240))
                .Add(New Point(640, 480))
                .Add(New Point(480, 360))
                .Add(New Point(141, 141))

            End With

            ' ArrayListアダプタによる並べ替え
            Dim arr As ArrayList = ArrayList.Adapter(.Items)

            arr.Sort(New PointComparer())

        End With

    End Sub

End Class
SortedをTrueにしたとき
[SortedをTrueにしたとき]
独自に定義した並べ替え
[独自に定義した並べ替え]

4.DataSourceプロパティによるアイテムの指定


 リストボックスのDataSourceプロパティにIListインターフェイスを実装したオブジェクト(ArrayやDataSetオブジェクト)を指定すると、Itemsプロパティからアイテムを追加しなくても、アイテムを表示させることができます。 つまり、既存の配列やコレクションをアイテムとしてリストボックスに表示できるのです。
 DataSourceプロパティにIListインターフェイスを実装したオブジェクトを指定した場合は、DisplayMember及びValueMemberプロパティも指定する必要があります。 DisplayMemberプロパティには、コレクションに含まれるオブジェクトのプロパティのうち、リストボックスに表示するべきプロパティの名前を指定します。 同様に、ValueMemberプロパティにはアイテムを指定する時に用いるプロパティ名を指定します。 ここで、DataSourceプロパティに値を設定した場合は、Itemsプロパティによってアイテムに対する操作を行うことはできません。
 次のコードでは、NickNameとFullNameという二つのプロパティを持ったMusumeクラスのオブジェクトをArrayListオブジェクトに格納し、それをリストボックスのDataSourceプロパティに指定しています。 また、リストボックスのDisplayMemberプロパティにはMusumeクラスのFullNameプロパティ、ValueMemberプロパティにはMusumeクラスのNickNameプロパティを指定しています。
ソースコード
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
' アイテムとしてリストボックスに追加されるクラス
Public Class Musume

    Private m_NickName As String
    Private m_FullName As String

    Public Sub New(ByVal nickName As String, ByVal fullName As String)

        m_NickName = nickName
        m_FullName = fullName

    End Sub

    Public ReadOnly Property NickName() As String
        Get
            Return m_NickName
        End Get
    End Property

    Public ReadOnly Property FullName() As String
        Get
            Return m_FullName
        End Get
    End Property

    Public Overrides Function ToString() As String

        Return m_FullName + "(" + m_NickName + ")"

    End Function

End Class

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' 元となるデータ
        Dim arr As New ArrayList()

        With arr

            .Add(New Musume("あいぼん", "加護亜依"))
            .Add(New Musume("チャーミー", "石川梨華"))
            .Add(New Musume("なっち", "安部なつみ"))
            .Add(New Musume("ミキティ", "藤本美貴"))
            .Add(New Musume("こんこん", "紺野あさ美"))

        End With

        With listBoxItems

            ' 選択モード
            .SelectionMode = SelectionMode.MultiExtended

            ' 元となるデータを設定
            .DataSource = arr

            ' リストボックスに表示されるプロパティ
            .DisplayMember = "FullName"

            ' アイテムを指定する時に用いるプロパティ
            .ValueMember = "NickName"

            ' すべての選択を解除
            .ClearSelected()

            ' いくつかのアイテムを、ValueMenberで指定したプロパティにより指定
            .SelectedValue = "チャーミー"
            .SelectedValue = "ミキティ"

            ' この操作はできない
            ' DataSourceに元となるデータを指定しているときは、直接アイテムを操作できない
            '.Items.Add(New Musume("まりっぺ", "矢口真里"))

        End With

    End Sub

End Class
実行結果
実行結果

 この結果を見てわかるとおり、リストボックスにはFullNameプロパティの値で表示され、ソースコードでアイテムを選択する場合はNickNameプロパティの値を用いています。
 また、DataSourceプロパティを指定していない他のリストボックスで選択されているアイテムを表示しようとすると次のようになります。 つまり、ToString()メソッドによって得られる文字列をアイテムとして表示します。
実行結果
実行結果