[VB.NET]DataGridViewでボタン列やテキスト列をソートする方法

こんにちは、今回はWindows Formsアプリケーションで、DataGridViewコントロールを使用している際に、ボタン列やテキスト列をヘッダークリックでソートする方法について解説します。特に、BindingList<T>をデータソースとして使用している場合の対処法も含めて詳しく説明します。

ソート可能な BindingList を実装する方法

以下に、BindingList<T> を拡張してソート機能を追加する方法を説明します。

1. SortableBindingList クラスを作成する

まず、BindingList<T> を継承し、ソート機能を実装した SortableBindingList<T> クラスを作成します。

Imports System.ComponentModel
Imports System.Reflection

Public Class SortableBindingList(Of T)
    Inherits BindingList(Of T)
    Implements IBindingListView

    Private isSortedValue As Boolean
    Private sortDirectionValue As ListSortDirection
    Private sortPropertyValue As PropertyDescriptor
    Private originalList As List(Of T)
    Private filterValue As String

    Public Sub New()
        MyBase.New()
        originalList = New List(Of T)()
    End Sub

    Public Sub New(list As IList(Of T))
        MyBase.New(list)
        originalList = New List(Of T)(list)
    End Sub

    Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
        Get
            Return True
        End Get
    End Property

    Protected Overrides ReadOnly Property IsSortedCore() As Boolean
        Get
            Return isSortedValue
        End Get
    End Property

    Protected Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor
        Get
            Return sortPropertyValue
        End Get
    End Property

    Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
        Get
            Return sortDirectionValue
        End Get
    End Property

    Protected Overrides Sub ApplySortCore(prop As PropertyDescriptor, direction As ListSortDirection)
        sortPropertyValue = prop
        sortDirectionValue = direction

        Dim list As List(Of T) = Me.Items

        list.Sort(Function(x, y)
                      Dim value1 As Object = prop.GetValue(x)
                      Dim value2 As Object = prop.GetValue(y)
                      Dim result As Integer = Comparer.Default.Compare(value1, value2)
                      If direction = ListSortDirection.Descending Then
                          result = -result
                      End If
                      Return result
                  End Function)

        isSortedValue = True
        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
    End Sub

    Protected Overrides Sub RemoveSortCore()
        Me.Items.Clear()
        For Each item In originalList
            Me.Items.Add(item)
        Next
        isSortedValue = False
    End Sub

    ' フィルタリングをサポートするために IBindingListView を実装
    Public ReadOnly Property SupportsFiltering As Boolean Implements IBindingListView.SupportsFiltering
        Get
            Return True
        End Get
    End Property

    Public Property Filter As String Implements IBindingListView.Filter
        Get
            Return filterValue
        End Get
        Set(value As String)
            filterValue = value
            UpdateFilter()
        End Set
    End Property

    Private Sub UpdateFilter()
        Dim items = originalList.AsEnumerable()

        If Not String.IsNullOrEmpty(filterValue) Then
            ' 簡易的なフィルタリングの実装(例: "Name = 'Alice'")
            Dim parts = filterValue.Split("="c)
            If parts.Length = 2 Then
                Dim propName = parts(0).Trim()
                Dim propValue = parts(1).Trim().Trim("'"c)

                Dim prop = TypeDescriptor.GetProperties(GetType(T))(propName)
                If prop IsNot Nothing Then
                    items = items.Where(Function(x) prop.GetValue(x).ToString() = propValue)
                End If
            End If
        End If

        Me.Items.Clear()
        For Each item In items
            Me.Items.Add(item)
        Next

        Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
    End Sub

    ' 他のメンバーの実装は省略していますが、必要に応じて追加してください

    Public ReadOnly Property SupportsAdvancedSorting As Boolean Implements IBindingListView.SupportsAdvancedSorting
        Get
            Return False
        End Get
    End Property

    Public Sub ApplySort(sorts As ListSortDescriptionCollection) Implements IBindingListView.ApplySort
        Throw New NotSupportedException()
    End Sub

    Public Sub RemoveFilter() Implements IBindingListView.RemoveFilter
        Me.Filter = Nothing
    End Sub

    Public ReadOnly Property SortDescriptions As ListSortDescriptionCollection Implements IBindingListView.SortDescriptions
        Get
            Return Nothing
        End Get
    End Property
End Class

のクラスのポイント:

  • BindingList<T> を継承し、ApplySortCore メソッドをオーバーライドしてソート機能を実装しています。
  • IBindingListView インターフェースを実装してフィルタリング機能もサポートしています(必要に応じて)。
  • isSortedValuesortDirectionValuesortPropertyValue などのフィールドでソート状態を管理しています。

2. SortableBindingList を使用する

SortableBindingList<T> クラスを使用して、データソースを作成します。

' 例として Employee クラスを定義
Public Class Employee
    Public Property EmployeeID As Integer
    Public Property Name As String
End Class
' データを作成
Dim employees As New SortableBindingList(Of Employee)()
employees.Add(New Employee() With {.EmployeeID = 1, .Name = "Alice"})
employees.Add(New Employee() With {.EmployeeID = 2, .Name = "Bob"})
employees.Add(New Employee() With {.EmployeeID = 3, .Name = "Charlie"})

' DataGridView にデータソースをバインド
DataGridView1.DataSource = employees

3. DataGridView の設定

列の SortModeAutomatic に設定します。

' 自動ソートを有効にする
DataGridView1.Columns("EmployeeID").SortMode = DataGridViewColumnSortMode.Automatic
DataGridView1.Columns("Name").SortMode = DataGridViewColumnSortMode.Automatic

まとめ

  • BindingList<T>のソート: SortableBindingList<T>を実装して、ソート機能を持たせます。

注意点:

  • データソースの選択: データソースがソートをサポートしているか確認し、必要に応じてカスタム実装を行います。
  • データの整合性: カスタムソートを実装する際、データの整合性に注意が必要です。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です