タグ: DataGridView

  • [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>を実装して、ソート機能を持たせます。

    注意点:

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