ソート機能を利用するための準備
それではソートができるように準備を始めましょう。DataGridViewでソート機能を簡単に利用するためには、まず次の2つのクラスを作成します。
IComparer
インターフェイスを実装したクラスBindingList
を拡張したクラス
IComparer
インナーフェイスを実装したクラスではオブジェクトの比較ロジックを実装します。このクラスのオブジェクトをList
オブジェクトのSort
メソッドのパラメータとすることで、その比較ロジックを利用した並び替えを行うことができます。オブジェクトの比較ではオブジェクト自体にCompare
メソッドを実装することも可能ですが、WSDLから生成されるオブジェクトに都度実装を行うのは手間がかかりますので、本稿ではIComparer
インターフェイスを実装しています。
BindingList
はオブジェクトのリストをバインドできるクラスですので、このクラスを拡張することで手軽にオブジェクトの比較を実装できるようにします。
IComparerの実装
ではまずIComparer
インターフェイスを実装したSampleComparer
クラスを作成してみましょう。
Imports System Imports System.ComponentModel Imports System.Collections.Generic Imports System.Reflection Public Class SampleComparer(Of T) Implements IComparer(Of T) Private _direction As ListSortDirection 'ソートの向き(昇順/降順) Private _property As PropertyDescriptor 'ソート項目 Public Sub New(ByVal prop As PropertyDescriptor, _ ByVal direction As ListSortDirection) Me._property = prop Me._direction = direction End Sub '同値の場合ゼロを返します。 Public Function Compare(ByVal objX As T, ByVal objY As T) _ As Integer Implements IComparer(Of T).Compare ' 比較対象のオブジェクトからクリックしたプロパティを取得します。 Dim valX As Object = Me.GetPropValue(objX, Me._property.Name) Dim valY As Object = Me.GetPropValue(objY, Me._property.Name) 'directionの値(昇順/降順)に応じて取得した値を比較します。 If (Me._direction = ListSortDirection.Ascending) Then Return Me.CompareAsc(valX, valY) Else Return Me.CompareDesc(valX, valY) End If End Function '昇順で比較を行います。 Private Function CompareAsc(ByVal valX As Object, _ ByVal valY As Object) As Integer Return valX.ToString.CompareTo(valY.ToString) End Function '降順で比較を行います。 Private Function CompareDesc(ByVal valX As Object, _ ByVal valY As Object) As Integer Return (Me.CompareAsc(valX, valY) * -1) End Function 'プロパティ値を取得します。 Private Function GetPropValue(ByVal val As T, _ ByVal prop As String) As Object Dim propInfo As PropertyInfo = val.GetType.GetProperty(prop) Return propInfo.GetValue(val, Nothing) End Function End Class
コンストラクタ
コンストラクタでは比較対象となるプロパティを指定するPropertyDescriptor
とソートの方向を指定するListSortDirection
をパラメータとしています。
PropertyDescriptor
はその名の通り、オブジェクトのプロパティについての情報定義です。プロパティのタイプ、値、ReadOnlyなどの情報を取得することができます。ここではプロパティの値を取得するために利用します。ListSortDirection
は並び替えの方向をあらわす列挙体でAscending
(昇順)、Descending
(降順)の値が定義されています。クリック時の昇順、降順ソートの方向を決めるために利用します。
メソッド
比較ロジックを実装するCompare
メソッドでは、第1パラメータと第2パラメータのオブジェクトからコンストラクタで指定された比較対象のプロパティ値を取得し、CompareTo
メソッドで大小比較を行っています。本稿では単純な例を挙げていますが、値がNothingであった場合の考慮など独自のロジックを実装することも可能です。
BindingListの拡張
次にBindingListを継承したSortBindingList
クラスを作成します。
Imports System Imports System.Collections.Generic Imports System.ComponentModel Public Class SortBindingList(Of T) Inherits BindingList(Of T) Private _isSorted As Boolean 'ソート済みか否かを識別します。 ' ソート項目を保持します。 Private _sortProperty As PropertyDescriptor ' ソート方向(昇順/降順)を保持します。 Private _sortDirection As ListSortDirection ' リストをバインドします。 Public Sub New(ByVal items As T()) CType(Me.Items, List(Of T)).AddRange(items) End Sub 'ソートが可能であることを公開します。 'このプロパティがtrueで公開されないとソートが実行できません。 Protected Overrides ReadOnly Property _ SupportsSortingCore() As Boolean Get Return True End Get End Property ' ソートを実行します。 Protected Overrides Sub ApplySortCore( _ ByVal prop As PropertyDescriptor, _ ByVal direction As ListSortDirection) 'ソートするためバインドされているリストを取得します。 Dim items As List(Of T) = TryCast(MyBase.Items, List(Of T)) 'リストがあった場合、 'Comparerを生成してソート項目と項目名を渡します。 If (Not items Is Nothing) Then Dim sc As New SampleComparer(Of T)(prop, direction) items.Sort(sc) 'ソート済みに設定します。 Me._isSorted = True Else Me._isSorted = False End If 'ソート結果、方向を保持しておきます。 Me._sortProperty = prop Me._sortDirection = direction 'リストが変更(ソート)されたことをイベント通知します。 Me.OnListChanged(New ListChangedEventArgs( _ ListChangedType.ItemMoved, prop)) End Sub End Class
コンストラクタ
コンストラクタではパラメータにバインドを行うクラスのリストを受け取っています。ジェネリックリストとなっていますのでクラス定義をパラメータとして受け取り、オブジェクトをキャストしています。
プロパティ
SupportsSortingCore
プロパティを実装し、ソート機能の利用可否を公開しています。Trueを返すように実装しないとソート機能を実行できません。
メソッド
ApplySortCore
メソッドでソートのロジックを実装します。まず第1パラメータのPropertyDescriptor
と第2パラメータのListSortDirection
を引数にして先ほど作成したSampleComparer
を生成します。そしてバインドしておいたリストのSort
メソッドのパラメータにSampleComparer
オブジェクトを渡すことで並び替え処理を実行しています。
ソートが完了したらListChangedイベントを発生させるために、OnListChanged
メソッドを実行します。パラメータにはListChangedEventArgs
を生成して渡しています。ListChangedEventArgs
クラスのコンストラクタでは、第1パラメータにリスト項目が移動したことをあらわすListChangedType
列挙体のItemMoved
、第2パラメータには変更されたリストのプロパティを指定しています。
作成したBindingListを利用してWebサービスをDataGridViewへバインドする。
バインドするには、BindingList
を今回作成したSortableBindingList
に入れ替えるだけです。ソートができるとヘッダーには矢印が表示され、ソートしている方向が示されます。
それでは作成したSampleExtentionをWebサービスに適用させましょう。
Public Class SampleForm Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'SortBindingListを介してDataSouceにバインドした場合 Me.BookBindingSource.DataSource = _ New SortBindingList(Of BookProxy.Book) _ (Me.BookService1.GetBookList) End Sub End Class
実行例
実行すると、ヘッダーをクリックする度にソートの昇順、降順が入れ替わります。このように作成したBindingList
を適用するだけで簡単にソート機能を実装することができました。
まとめ
本稿で説明したクラスを利用することで、オブジェクト配列でバインドされるデータに対しても簡単にソート機能を利用することができるようになりました。.NET Framework 2.0から登場したBindingList
にはこれ以外にも検索機能を実装することなどができます。開発者の方々が使いやすいように拡張していくことで、より簡潔なコーディングで高機能な一覧表示を実装することができるようになるでしょう。
参考資料
- MSDN 『Web Services Developer Center』(日本語)
- MSDNライブラリ 『XML Web サービスの作成とアクセスに関するチュートリアル』
- MSDNライブラリ 『IComparerインターフェイス』
- MSDNライブラリ 『BindingListクラス』
- MSDNライブラリ 『ListSortDirection列挙体』
- MSDNライブラリ 『PropertyDescriptorクラス』
- MSDNライブラリ 『ListChangedEventArgsクラス』
- MSDNライブラリ 『ListChangedType列挙体』