今回はIComparableとIComparerを使ったソートについて見てみます。 それに先だって、ArrayListクラスを用いて最も簡単なソートを行ってみようと思います。 ここではArrayListの詳細な説明はしませんが、ArrayListは通常の配列とは異なり、好きなときにアイテムを追加・削除でき、動的にアイテム数を変えることができるという特徴があります。 また、このArrayListクラスから配列を作成することもできます。 それでは早速、ArrayListとそれを用いたソートのサンプルコードを組んでみます。
Imports System Imports System.Collections Public Class Comparison Public Shared Function Main(ByVal args() As String) As Integer Dim arr As New ArrayList() arr.Add("GOTO Maki") arr.Add("KAGO Ai") arr.Add("TSUJI Nozomi") arr.Add("MATSUURA Aya") arr.Add("KONNO Asami") arr.Add("FUJIMOTO Miki") Dim name As String Console.WriteLine(" Before sorting...") For Each name In arr Console.WriteLine(name) Next arr.Sort() Console.WriteLine() Console.WriteLine(" After sorting...") For Each name In arr Console.WriteLine(name) Next Return 0 End Function End Class
Before sorting... GOTO Maki KAGO Ai TSUJI Nozomi MATSUURA Aya KONNO Asami FUJIMOTO Miki After sorting... FUJIMOTO Miki GOTO Maki KAGO Ai KONNO Asami MATSUURA Aya TSUJI Nozomi Press any key to continue
10行目から15行目でArrayListにソートすべきアイテムを追加しています。 その後のコードではソートする前にアイテムを列挙し、ソートした後にまたアイテムを列挙しています。 実際にソートを行っているのは28行目のSort()メソッドです。 実行結果のとおり、ソート前には追加された順に並んでいるのに対し、ソート後にはアイテムとして追加した文字列がアルファベット順に並んでいます。 この例ではソート結果が分かりやすいようにアルファベットを用いましたが、日本語文字などでも適切にソートされます。
先ほどはArrayListに文字列を追加しましたが、数値も追加できます。 ソースコードは割愛しますが、先ほどのソースコードにおいてアイテムを追加した部分を文字列から数値(IntegerとDouble)に変えて実行した結果です。
Before sorting... 3 1 4 1 5 9 2 7 After sorting... 1 1 2 3 4 5 7 9 Press any key to continue
Before sorting... 3.1415927 1.7320508 2.7182818 1.41421356 2.2360679 After sorting... 1.41421356 1.7320508 2.2360679 2.7182818 3.1415927 Press any key to continue
この調子で行けばどのような型でもソートできそうな感じなのですが、実際にはそうはいきません。 次のコードはTestClassというクラスを新たに作成し、そのインスタンスをArrayListに追加してソートさせるコードです。
Imports System
Imports System.Collections
Public Class TestClass
Public str As String
Public Sub New(ByVal str As String)
Me.str = str
End Sub
Public Overrides Function ToString() As String
Return Me.str
End Function
End Class
Public Class Comparison
Public Shared Function Main(ByVal args() As String) As Integer
Dim arr As New ArrayList()
arr.Add(New TestClass("どんな型でも"))
arr.Add(New TestClass("ArrayListは"))
arr.Add(New TestClass("ソートして"))
arr.Add(New TestClass("くれるだろうか"))
Dim inst As TestClass
Console.WriteLine(" Before sorting...")
For Each inst In arr
Console.WriteLine(inst)
Next
arr.Sort()
Console.WriteLine()
Console.WriteLine(" After sorting...")
For Each inst In arr
Console.WriteLine(inst)
Next
Return 0
End Function
End Class
Before sorting... どんな型でも ArrayListは ソートして くれるだろうか ハンドルされていない例外 : System.InvalidOperationException: 指定された ICompare r が例外をスローしました。 ---> System.ArgumentException: 少なくとも 1 つのオブ ジェクトで IComparable を実装しなければなりません。 at System.Collections.Comparer.Compare(Object a, Object b) at System.SorterObjectArray.QuickSort(Int32 left, Int32 right) --- 内部例外スタック トレースの終わり --- at System.SorterObjectArray.QuickSort(Int32 left, Int32 right) at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, ICom parer comparer) at System.Collections.ArrayList.Sort(Int32 index, Int32 count, IComparer comp arer) at System.Collections.ArrayList.Sort() at SampleConsoleApplication.MainModule.Main() in D:\Visual Basic .NET Program \SampleConsoleApplication\Module1.vb:line 148 Press any key to continue
実行結果のように、Sort()メソッドを呼び出したとたん例外エラーが発生し、ソートに失敗します。 IntegerやString型は数値・文字列なので現実的に「比較」できるのですが、TestClassのような場合文字列が含まれているとはいえ、どのように比較していいのかわかりません。 そのためこのような例外エラーが発生します。
しかし、この例外エラーのメッセージをよく見てみると、
ハンドルされていない例外 : System.InvalidOperationException: 指定された IComparer が例外をスローしました。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。
というように書いてあります。 実はソートできるかできないかを決めるカギはこのIComparableインターフェイスにあるのです。 つまり、ソートを行うには、そのクラスがIComparableを実装していればいいのです。 Integer型のエイリアスであるSystem.Int32構造体は IComparableなどを含むいくつかのインターフェイスを実装しています。 そのためにソートができたのです。
それでは早速IComparableインターフェイスを実装してみます。 IComparableインターフェイスを実装する場合は一つのメソッドCompareTo()を実装しなければなりません。 このメソッドの詳細は次のようなものです。
このことを考慮して、IComparableを実装したクラスの例を次に示します。 このクラスでは、名前・誕生日・年齢をメンバとして持つクラスで、CompareTo()メソッドでは年齢によって比較を行います。
Imports System Imports System.Collections Public Class Girl ' IComparableインターフェイスをインプリメント Implements IComparable ' フィールド Public Name As String Public Age As Integer Public BirthDate As DateTime ' コンストラクタ Public Sub New() End Sub Public Sub New(ByVal Name As String, ByVal BirthDate As DateTime) MyClass.Name = Name MyClass.BirthDate = BirthDate ' 年齢を計算 MyClass.Age = DateTime.Now.Year - BirthDate.Year Dim birthDayInThisYear As New DateTime(DateTime.Now.Year, BirthDate.Month, BirthDate.Day) If birthDayInThisYear > DateTime.Now Then Me.Age -= 1 End Sub ' ToString()メソッドをオーバーライド Public Overrides Function ToString() As String Return Name + "(" + BirthDate.ToLongDateString() + "生まれ, " + Age.ToString() + "歳)" End Function ' IComparable.CompareTo()メソッドを実装 Public Function CompareTo(ByVal x As Object) As Integer Implements IComparable.CompareTo ' 渡されたオブジェクトの型を確認 If TypeOf x Is Girl Then Dim g As Girl = CType(x, Girl) If Me.Age > g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeより大きいとき Return 1 ElseIf Me.Age = g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeと同じとき Return 0 ElseIf Me.Age < g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeより小さいとき Return -1 End If End If ' 比較できないとき Return 0 End Function End ClassDateTime.Now Then Me.Age -= 1 End Sub ' ToString()メソッドをオーバーライド Public Overrides Function ToString() As String Return Name + "(" + BirthDate.ToLongDateString() + "生まれ, " + Age.ToString() + "歳)" End Function ' IComparable.CompareTo()メソッドを実装 Public Function CompareTo(ByVal x As Object) As Integer Implements IComparable.CompareTo ' 渡されたオブジェクトの型を確認 If TypeOf x Is Girl Then Dim g As Girl = CType(x, Girl) If Me.Age > g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeより大きいとき Return 1 ElseIf Me.Age = g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeと同じとき Return 0 ElseIf Me.Age < g.Age Then ' このインスタンスのAgeが渡されたインスタンスのAgeより小さいとき Return -1 End If End If ' 比較できないとき Return 0 End Function End Class]]>
CompareTo()メソッドでは引数の型がObject型なので一度渡されたオブジェクトの型を確認してから、自分のクラス型であるGirl型にキャストします。 その後渡されたインスタンスと自分自身を比較して適切な値を返してやります。 ArrayListにGirl型以外のインスタンスが格納されていた場合など、比較できない型が渡された場合に備え、そのような場合には等価であると定義しておきます。 実際にこのクラスを用いた例が次のコードです。
Imports System Imports System.Collections Public Class Comparison Public Shared Function Main(ByVal args() As String) As Integer Dim arr As New ArrayList() arr.Add(New Girl("後藤真希", New DateTime(1985, 9, 23))) arr.Add(New Girl("加護亜依", New DateTime(1988, 2, 7))) arr.Add(New Girl("辻希美", New DateTime(1987, 6, 17))) arr.Add(New Girl("松浦亜弥", New DateTime(1986, 6, 25))) arr.Add(New Girl("紺野あさ美", New DateTime(1987, 5, 7))) arr.Add(New Girl("藤本美貴", New DateTime(1985, 2, 26))) Dim g As Girl Console.WriteLine(" Before sorting...") For Each g In arr Console.WriteLine(g) Next arr.Sort() Console.WriteLine(" After sorting...") For Each g In arr Console.WriteLine(g) Next Return 0 End Function End Class
Before sorting...
後藤真希(1985年9月23日生まれ, 18歳)
加護亜依(1988年2月7日生まれ, 15歳)
辻希美(1987年6月17日生まれ, 16歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
藤本美貴(1985年2月26日生まれ, 18歳)
After sorting...
加護亜依(1988年2月7日生まれ, 15歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
辻希美(1987年6月17日生まれ, 16歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
藤本美貴(1985年2月26日生まれ, 18歳)
後藤真希(1985年9月23日生まれ, 18歳)
Press any key to continue
この結果を見るとわかるとおり、CompareTo()では年齢という数値だけを比較しているので、誕生日の順番は考慮されていません。 誕生日で比較させる場合にはCompareTo()メソッド内における比較処理を誕生日で行うように変えてやります。 その場合のソースコードと実行結果は次のようになります。
Imports System Imports System.Collections Public Class Girl ' IComparableインターフェイスをインプリメント Implements IComparable ' IComparable.CompareTo()メソッドを実装 Public Function CompareTo(ByVal x As Object) As Integer Implements IComparable.CompareTo ' 渡されたオブジェクトの型を確認 If TypeOf x Is Girl Then Dim g As Girl = CType(x, Girl) If Me.BirthDate > g.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateより大きいとき Return 1 ElseIf Me.BirthDate = g.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateと同じとき Return 0 ElseIf Me.BirthDate < g.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateより小さいとき Return -1 End If End If ' 比較できないとき Return 0 End Function End Classg.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateより大きいとき Return 1 ElseIf Me.BirthDate = g.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateと同じとき Return 0 ElseIf Me.BirthDate < g.BirthDate Then ' このインスタンスのBirthDateが渡されたインスタンスのBirthDateより小さいとき Return -1 End If End If ' 比較できないとき Return 0 End Function End Class]]>
Before sorting...
後藤真希(1985年9月23日生まれ, 18歳)
加護亜依(1988年2月7日生まれ, 15歳)
辻希美(1987年6月17日生まれ, 16歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
藤本美貴(1985年2月26日生まれ, 18歳)
After sorting...
藤本美貴(1985年2月26日生まれ, 18歳)
後藤真希(1985年9月23日生まれ, 18歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
辻希美(1987年6月17日生まれ, 16歳)
加護亜依(1988年2月7日生まれ, 15歳)
Press any key to continue
IComparableインターフェイスをクラスに実装すれば比較を行えるようになるのですが、先ほどの例のようにソート方法を変えたい場合はコードを書き換えなければなりません。 ソート方法が誕生日だけでいい場合は問題ありませんが、年齢順や名前順でソートしたくなる場合が出てきます。 そのような場合はIComparableインターフェイスではなくIComaparerインターフェイスを用います。
このインターフェイスは比較を行いたいクラスに実装するのではなく、比較を行わせるクラスに実装します。 つまり、比較対照と比較方法を区別することができるのです。 IComparableの時と同様に、IComparerを実装する場合はCompare()メソッドを実装する必要があります。 その詳細は次のようなものです。
Compare()メソッドはCompareTo()とは異なり二つのオブジェクトを引数にとります。 CompareTo()メソッドは自分自身とを比較したのに対し、Compare()メソッドでは自分自身は比較方法を定義するクラスで、比較対照ではないからです。 後の戻り値は CompareTo()と大差ありません。 これを実装したクラスの例を示します。
Imports System Imports System.Collections ' 比較方法を設定する列挙型 Public Enum GirlComparisonMode As Integer ByName = 0 ByBirthDate = 1 ByAge = 2 End Enum ' Girlクラスの比較を行うクラス Public Class GirlComparer Implements IComparer ' 比較方法 Public ComparisonMode As GirlComparisonMode ' コンストラクタ Public Sub New() ComparisonMode = GirlComparisonMode.ByName End Sub ' 比較メソッド Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare ' オブジェクトの型を確認 If TypeOf x Is Girl AndAlso TypeOf y Is Girl Then Dim gx As Girl = CType(x, Girl) Dim gy As Girl = CType(y, Girl) Select Case ComparisonMode Case GirlComparisonMode.ByName ' 名前による比較 If gx.Name > gy.Name Then Return 1 ElseIf gx.Name = gy.Name Then Return 0 ElseIf gx.Name < gy.Name Then Return -1 End If Case GirlComparisonMode.ByBirthDate ' 誕生日による比較 If gx.BirthDate > gy.BirthDate Then Return 1 ElseIf gx.BirthDate = gy.BirthDate Then Return 0 ElseIf gx.BirthDate < gy.BirthDate Then Return -1 End If Case GirlComparisonMode.ByAge ' 年齢による比較 Return gx.Age - gy.Age End Select End If ' 比較できない場合 Return 0 End Function End Class ' Girlクラス Public Class Girl ' フィールド Public Name As String Public Age As Integer Public BirthDate As DateTime ' コンストラクタ Public Sub New() End Sub Public Sub New(ByVal Name As String, ByVal BirthDate As DateTime) MyClass.Name = Name MyClass.BirthDate = BirthDate ' 年齢を計算 MyClass.Age = DateTime.Now.Year - BirthDate.Year Dim birthDayInThisYear As New DateTime(DateTime.Now.Year, BirthDate.Month, BirthDate.Day) If birthDayInThisYear > DateTime.Now Then Me.Age -= 1 End Sub ' ToString()メソッドをオーバーライド Public Overrides Function ToString() As String Return Name + "(" + BirthDate.ToLongDateString() + "生まれ, " + Age.ToString() + "歳)" End Function ' CompareTo()メソッドは必要ない End Class Public Class Comparison Public Shared Function Main(ByVal args() As String) As Integer Dim arr As New ArrayList() arr.Add(New Girl("後藤真希", New DateTime(1985, 9, 23))) arr.Add(New Girl("加護亜依", New DateTime(1988, 2, 7))) arr.Add(New Girl("辻希美", New DateTime(1987, 6, 17))) arr.Add(New Girl("松浦亜弥", New DateTime(1986, 6, 25))) arr.Add(New Girl("紺野あさ美", New DateTime(1987, 5, 7))) arr.Add(New Girl("藤本美貴", New DateTime(1985, 2, 26))) Dim g As Girl Console.WriteLine(" Before sorting...") For Each g In arr Console.WriteLine(g) Next Dim i As Integer Dim c As New GirlComparer() For i = 0 To 2 c.ComparisonMode = i arr.Sort(c) Console.WriteLine(" Sorted " + c.ComparisonMode.ToString()) For Each g In arr Console.WriteLine(g) Next Next Return 0 End Function End Classgy.Name Then Return 1 ElseIf gx.Name = gy.Name Then Return 0 ElseIf gx.Name < gy.Name Then Return -1 End If Case GirlComparisonMode.ByBirthDate ' 誕生日による比較 If gx.BirthDate > gy.BirthDate Then Return 1 ElseIf gx.BirthDate = gy.BirthDate Then Return 0 ElseIf gx.BirthDate < gy.BirthDate Then Return -1 End If Case GirlComparisonMode.ByAge ' 年齢による比較 Return gx.Age - gy.Age End Select End If ' 比較できない場合 Return 0 End Function End Class ' Girlクラス Public Class Girl ' フィールド Public Name As String Public Age As Integer Public BirthDate As DateTime ' コンストラクタ Public Sub New() End Sub Public Sub New(ByVal Name As String, ByVal BirthDate As DateTime) MyClass.Name = Name MyClass.BirthDate = BirthDate ' 年齢を計算 MyClass.Age = DateTime.Now.Year - BirthDate.Year Dim birthDayInThisYear As New DateTime(DateTime.Now.Year, BirthDate.Month, BirthDate.Day) If birthDayInThisYear > DateTime.Now Then Me.Age -= 1 End Sub ' ToString()メソッドをオーバーライド Public Overrides Function ToString() As String Return Name + "(" + BirthDate.ToLongDateString() + "生まれ, " + Age.ToString() + "歳)" End Function ' CompareTo()メソッドは必要ない End Class Public Class Comparison Public Shared Function Main(ByVal args() As String) As Integer Dim arr As New ArrayList() arr.Add(New Girl("後藤真希", New DateTime(1985, 9, 23))) arr.Add(New Girl("加護亜依", New DateTime(1988, 2, 7))) arr.Add(New Girl("辻希美", New DateTime(1987, 6, 17))) arr.Add(New Girl("松浦亜弥", New DateTime(1986, 6, 25))) arr.Add(New Girl("紺野あさ美", New DateTime(1987, 5, 7))) arr.Add(New Girl("藤本美貴", New DateTime(1985, 2, 26))) Dim g As Girl Console.WriteLine(" Before sorting...") For Each g In arr Console.WriteLine(g) Next Dim i As Integer Dim c As New GirlComparer() For i = 0 To 2 c.ComparisonMode = i arr.Sort(c) Console.WriteLine(" Sorted " + c.ComparisonMode.ToString()) For Each g In arr Console.WriteLine(g) Next Next Return 0 End Function End Class]]>
Before sorting...
後藤真希(1985年9月23日生まれ, 18歳)
加護亜依(1988年2月7日生まれ, 15歳)
辻希美(1987年6月17日生まれ, 16歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
藤本美貴(1985年2月26日生まれ, 18歳)
Sorted ByName
加護亜依(1988年2月7日生まれ, 15歳)
後藤真希(1985年9月23日生まれ, 18歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
藤本美貴(1985年2月26日生まれ, 18歳)
辻希美(1987年6月17日生まれ, 16歳)
Sorted ByBirthDate
藤本美貴(1985年2月26日生まれ, 18歳)
後藤真希(1985年9月23日生まれ, 18歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
辻希美(1987年6月17日生まれ, 16歳)
加護亜依(1988年2月7日生まれ, 15歳)
Sorted ByAge
加護亜依(1988年2月7日生まれ, 15歳)
紺野あさ美(1987年5月7日生まれ, 16歳)
辻希美(1987年6月17日生まれ, 16歳)
松浦亜弥(1986年6月25日生まれ, 17歳)
藤本美貴(1985年2月26日生まれ, 18歳)
後藤真希(1985年9月23日生まれ, 18歳)
Press any key to continue
実際に比較方法を指定するためには、IComparerを実装したクラスのインスタンスを作成し、Sort()メソッドを呼び出すときにそのインスタンスを引数として渡します。 この例ではForステートメントにより定義した3種類の比較方法を使用してSort()メソッドを呼び出し、その結果を表示しています。
この例のように比較方法を外部クラスで指定する場合は、比較対照となるクラスはIComparableを実装している必要はありません。 このソースコードでもGirlクラスはIComparableを実装していません。 それに変わって、IComparerを実装したクラスでは適切な比較方法を定義していなければなりません。
ListViewコントロールはその特性上、最もよくソートを行うと思います。 また、ソート方法も場合によって様々です。 ListViewコントロールで独自のソート方法を定義するためにIComparerを適用したサンプルを作ってみました。 コメントをふってあるので詳しい説明はしませんが、いくつか応用的なコードもあるのでぜひ参考にして下さい。
' 比較方法を指定する列挙型 Public Enum ComparisonMode ByName = 0 ByBirthDate = 1 ByAge = 2 ByBloodType = 3 End Enum ' 比較を行うクラス Public Class Comparer ' IComparerインターフェイスをインプリメント Implements IComparer ' 比較方法 Public ComparisonMode As ComparisonMode ' 昇順か降順か ( System.Windows.Forms.SortOrder列挙型を流用 ) Public SortOrder As SortOrder ' コンストラクタ Public Sub New() End Sub Public Sub New(ByVal comparisonMode As ComparisonMode, ByVal sortOrder As SortOrder) Me.ComparisonMode = comparisonMode Me.SortOrder = sortOrder End Sub ' 比較を行うメソッド Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare ' 規定の戻り値 Dim intCompareResult As Integer = 0 ' ソート無しの時 If SortOrder = SortOrder.None Then Return intCompareResult ' 比較対象がListViewItemの時 ( この型で渡される ) If TypeOf x Is ListViewItem AndAlso TypeOf y Is ListViewItem Then ' ListItem型へキャスト Dim itemX As ListViewItem = CType(x, ListViewItem) Dim itemY As ListViewItem = CType(y, ListViewItem) ' 比較方法とアイテムから比較すべき文字列を取得 Dim textX As String = itemX.SubItems(ComparisonMode).Text Dim textY As String = itemY.SubItems(ComparisonMode).Text ' 比較方法別の処理 Select Case ComparisonMode Case ComparisonMode.ByAge ' 年齢による比較 Dim ageX As Integer = Integer.Parse(textX) Dim ageY As Integer = Integer.Parse(textY) ' その差を結果とする intCompareResult = ageX - ageY Case ComparisonMode.ByBirthDate ' 生年月日による比較 Dim dtmX As DateTime = DateTime.Parse(textX) Dim dtmY As DateTime = DateTime.Parse(textY) If dtmX > dtmY Then intCompareResult = 1 ElseIf dtmX = dtmY Then intCompareResult = 0 ElseIf dtmX < dtmY Then intCompareResult = -1 End If Case ComparisonMode.ByBloodType, ComparisonMode.ByName ' 名前・血液型による比較 ( String型のCompare()メソッドをを流用する ) intCompareResult = String.Compare(textX, textY) End Select ' 昇順降順の設定による戻り値の符号反転 If SortOrder = SortOrder.Descending Then intCompareResult = -intCompareResult End If ' 戻り値 Return intCompareResult End Function End Class ' フォーム Public Class ComparisonDialog Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 End Sub ' Form は dispose をオーバーライドしてコンポーネント一覧を消去します。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 ' Windows フォーム デザイナを使って変更してください。 ' コード エディタは使用しないでください。 <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() ' 'ComparisonDialog ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(512, 302) Me.Name = "ComparisonDialog" Me.Text = "ListViewとIComparerのサンプル" End Sub #End Region ' リストビュー Private listView As listView = Nothing ' リストビューソート順 Private listViewSortOrder As SortOrder ' フォームのLoadイベントハンドラ Private Sub ComparisonDialog_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' リストビューのインスタンス listView = New ListView() ' コントロールリストに追加 Me.Controls.Add(listView) ' 各プロパティを設定 With listView .Location = New Point(20, 20) ' 位置 .Size = New Size(Me.ClientSize.Width - 40, Me.ClientSize.Height - 40) 'サイズ .View = View.Details ' ビューのタイプを「詳細」にする .HeaderStyle = ColumnHeaderStyle.Clickable ' 項目タブをクリック可能にしておく ' イベントハンドラを追加 AddHandler .ColumnClick, AddressOf listView_ColumnClick End With ' 項目タブを作る With listView.Columns .Add("名前", listView.Width * 0.4, HorizontalAlignment.Left) .Add("生年月日", listView.Width * 0.25, HorizontalAlignment.Left) .Add("年齢", listView.Width * 0.15, HorizontalAlignment.Left) .Add("血液型", listView.Width * 0.15, HorizontalAlignment.Left) End With ' アイテムを追加する AddListViewItem(listView, "飯田圭織", New DateTime(1981, 8, 8), "A") AddListViewItem(listView, "安倍なつみ", New DateTime(1981, 8, 10), "A") AddListViewItem(listView, "保田圭", New DateTime(1980, 12, 6), "A") AddListViewItem(listView, "矢口真里", New DateTime(1983, 1, 20), "A") AddListViewItem(listView, "吉澤ひとみ", New DateTime(1985, 4, 12), "O") AddListViewItem(listView, "石川梨華", New DateTime(1985, 1, 19), "A") AddListViewItem(listView, "辻希美", New DateTime(1987, 6, 17), "O") AddListViewItem(listView, "加護亜依", New DateTime(1988, 2, 7), "AB") AddListViewItem(listView, "小川麻琴", New DateTime(1987, 10, 29), "O") AddListViewItem(listView, "新垣里沙", New DateTime(1988, 10, 20), "B") AddListViewItem(listView, "高橋愛", New DateTime(1986, 9, 14), "A") AddListViewItem(listView, "紺野あさ美", New DateTime(1987, 5, 7), "B") AddListViewItem(listView, "後藤真希", New DateTime(1985, 9, 23), "O") AddListViewItem(listView, "松浦亜弥", New DateTime(1986, 6, 25), "B") AddListViewItem(listView, "藤本美貴", New DateTime(1985, 2, 26), "A") ' ソート順を設定しておく listViewSortOrder = SortOrder.Ascending End Sub ' リストビューにアイテムを追加する Private Sub AddListViewItem(ByVal list As listView, ByVal name As String, ByVal birthDate As DateTime, ByVal bloodType As String) ' 新たなアイテムのインスタンスを作成 Dim item As New ListViewItem() ' 年齢を計算 Dim age As Integer = DateTime.Now.Year - birthDate.Year Dim birthDayInThisYear As New DateTime(DateTime.Now.Year, birthDate.Month, birthDate.Day) If birthDayInThisYear > DateTime.Now Then age -= 1 ' アイテムのプロパティを設定 ( アイテムは文字列としてしか登録できない ) With item .Text = name .SubItems.Add(birthDate.ToShortDateString()) .SubItems.Add(age.ToString()) .SubItems.Add(bloodType) End With ' リストに追加 list.Items.Add(item) End Sub ' リストビューのColumnClickイベントハンドラ ( 項目タブをクリックする度に呼び出される ) Private Sub listView_ColumnClick(ByVal sender As Object, ByVal e As ColumnClickEventArgs) ' 昇順・降順を切り替え If listViewSortOrder = SortOrder.Ascending Then listViewSortOrder = SortOrder.Descending Else listViewSortOrder = SortOrder.Ascending End If ' 比較担当を作成 ( e.Columnはクリックされた項目タブのインデックス ) Dim comp As New Comparer(e.Column, listViewSortOrder) ' 比較開始 ( このプロパティにICopmarerを指定すると自動的にソートが開始される ) listView.ListViewItemSorter = comp End Sub End ClassdtmY Then intCompareResult = 1 ElseIf dtmX = dtmY Then intCompareResult = 0 ElseIf dtmX < dtmY Then intCompareResult = -1 End If Case ComparisonMode.ByBloodType, ComparisonMode.ByName ' 名前・血液型による比較 ( String型のCompare()メソッドをを流用する ) intCompareResult = String.Compare(textX, textY) End Select ' 昇順降順の設定による戻り値の符号反転 If SortOrder = SortOrder.Descending Then intCompareResult = -intCompareResult End If ' 戻り値 Return intCompareResult End Function End Class ' フォーム Public Class ComparisonDialog Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 End Sub ' Form は dispose をオーバーライドしてコンポーネント一覧を消去します。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 ' Windows フォーム デザイナを使って変更してください。 ' コード エディタは使用しないでください。Private Sub InitializeComponent() ' 'ComparisonDialog ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(512, 302) Me.Name = "ComparisonDialog" Me.Text = "ListViewとIComparerのサンプル" End Sub #End Region ' リストビュー Private listView As listView = Nothing ' リストビューソート順 Private listViewSortOrder As SortOrder ' フォームのLoadイベントハンドラ Private Sub ComparisonDialog_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' リストビューのインスタンス listView = New ListView() ' コントロールリストに追加 Me.Controls.Add(listView) ' 各プロパティを設定 With listView .Location = New Point(20, 20) ' 位置 .Size = New Size(Me.ClientSize.Width - 40, Me.ClientSize.Height - 40) 'サイズ .View = View.Details ' ビューのタイプを「詳細」にする .HeaderStyle = ColumnHeaderStyle.Clickable ' 項目タブをクリック可能にしておく ' イベントハンドラを追加 AddHandler .ColumnClick, AddressOf listView_ColumnClick End With ' 項目タブを作る With listView.Columns .Add("名前", listView.Width * 0.4, HorizontalAlignment.Left) .Add("生年月日", listView.Width * 0.25, HorizontalAlignment.Left) .Add("年齢", listView.Width * 0.15, HorizontalAlignment.Left) .Add("血液型", listView.Width * 0.15, HorizontalAlignment.Left) End With ' アイテムを追加する AddListViewItem(listView, "飯田圭織", New DateTime(1981, 8, 8), "A") AddListViewItem(listView, "安倍なつみ", New DateTime(1981, 8, 10), "A") AddListViewItem(listView, "保田圭", New DateTime(1980, 12, 6), "A") AddListViewItem(listView, "矢口真里", New DateTime(1983, 1, 20), "A") AddListViewItem(listView, "吉澤ひとみ", New DateTime(1985, 4, 12), "O") AddListViewItem(listView, "石川梨華", New DateTime(1985, 1, 19), "A") AddListViewItem(listView, "辻希美", New DateTime(1987, 6, 17), "O") AddListViewItem(listView, "加護亜依", New DateTime(1988, 2, 7), "AB") AddListViewItem(listView, "小川麻琴", New DateTime(1987, 10, 29), "O") AddListViewItem(listView, "新垣里沙", New DateTime(1988, 10, 20), "B") AddListViewItem(listView, "高橋愛", New DateTime(1986, 9, 14), "A") AddListViewItem(listView, "紺野あさ美", New DateTime(1987, 5, 7), "B") AddListViewItem(listView, "後藤真希", New DateTime(1985, 9, 23), "O") AddListViewItem(listView, "松浦亜弥", New DateTime(1986, 6, 25), "B") AddListViewItem(listView, "藤本美貴", New DateTime(1985, 2, 26), "A") ' ソート順を設定しておく listViewSortOrder = SortOrder.Ascending End Sub ' リストビューにアイテムを追加する Private Sub AddListViewItem(ByVal list As listView, ByVal name As String, ByVal birthDate As DateTime, ByVal bloodType As String) ' 新たなアイテムのインスタンスを作成 Dim item As New ListViewItem() ' 年齢を計算 Dim age As Integer = DateTime.Now.Year - birthDate.Year Dim birthDayInThisYear As New DateTime(DateTime.Now.Year, birthDate.Month, birthDate.Day) If birthDayInThisYear > DateTime.Now Then age -= 1 ' アイテムのプロパティを設定 ( アイテムは文字列としてしか登録できない ) With item .Text = name .SubItems.Add(birthDate.ToShortDateString()) .SubItems.Add(age.ToString()) .SubItems.Add(bloodType) End With ' リストに追加 list.Items.Add(item) End Sub ' リストビューのColumnClickイベントハンドラ ( 項目タブをクリックする度に呼び出される ) Private Sub listView_ColumnClick(ByVal sender As Object, ByVal e As ColumnClickEventArgs) ' 昇順・降順を切り替え If listViewSortOrder = SortOrder.Ascending Then listViewSortOrder = SortOrder.Descending Else listViewSortOrder = SortOrder.Ascending End If ' 比較担当を作成 ( e.Columnはクリックされた項目タブのインデックス ) Dim comp As New Comparer(e.Column, listViewSortOrder) ' 比較開始 ( このプロパティにICopmarerを指定すると自動的にソートが開始される ) listView.ListViewItemSorter = comp End Sub End Class]]>