繁体   English   中英

DataGridView上的动态多列排序

[英]Dynamic Multiple Column Sort on a DataGridView

我们有一个DataGridView的自定义实现(它没有做很多事情,仅处理格式化等)。 所有DataGridView控件都绑定到自定义对象列表( System.Generic.List(Of MyObjectName) )。 我的任务是使所有这些网格在多列上可排序(我将从两列开始,然后根据需要再次更改)。

当前,通过调用将List(Of T)转换为IBindingList的方法来处理这些网格的排序。 然后完成排序:

Private Sub DoSort()
    sortedList.Clear()
    If sortBy Is Nothing Then
        For Each obj As Object In BaseList
            sortedList.Add(New ListItem(obj, obj))
        Next
    Else
        If (sortBy.PropertyType Is GetType(Date)) Then
            For Each obj As Object In BaseList
                sortedList.Add(New ListItem(DirectCast(sortBy.GetValue(obj), Date), obj))
            Next
        ElseIf (sortBy.PropertyType Is GetType(Integer)) Then
            For Each obj As Object In BaseList
                sortedList.Add(New ListItem(DirectCast(sortBy.GetValue(obj), Integer), obj))
            Next
        Else
            For Each obj As Object In BaseList
                sortedList.Add(New ListItem(sortBy.GetValue(obj), obj))
            Next
        End If
    End If
    sortedList.Sort()
    m_isSorted = True
    RaiseEvent ListChanged(Me, New ListChangedEventArgs(ListChangedType.Reset, 0))
End Sub

我尝试但未能在此添加另一列。 当要求将多列排序添加到特定网格时,我作弊并直接对该列表进行排序。 然后,我尝试使用相同的代码来修改我们的自定义网格控件:

Protected Overrides Sub OnColumnHeaderMouseClick(e As DataGridViewCellMouseEventArgs)
    MyBase.OnColumnHeaderMouseClick(e)

    If TypeOf (Me.DataSource) Is IList AndAlso Me.DataSource.GetType().IsGenericType Then
        If _strSortcolumn02 = "" Then
            _strSortColumn01 = Me.Columns(e.ColumnIndex).Name
            _strSortcolumn02 = Me.Columns(e.ColumnIndex).Name
        Else
            _strSortColumn01 = _strSortcolumn02
            _strSortcolumn02 = Me.Columns(e.ColumnIndex).Name
        End If

        If Me.Columns(e.ColumnIndex).HeaderCell.SortGlyphDirection = SortOrder.Ascending Then
            _soSortDirection = Windows.Forms.SortOrder.Descending
        Else
            _soSortDirection = Windows.Forms.SortOrder.Ascending
        End If

        'Dim tType As Type = Me.DataSource.GetType()
        'Dim typeName As String = String.Format("System.Collections.Generic.List`1[[{0}]], mscorlib", tType.AssemblyQualifiedName)
        'Dim lst As New List(Of Type.GetType(typeName))
        Dim lst As Object = Me.DataSource

        If _soSortDirection = Windows.Forms.SortOrder.Ascending Then
            Me.DataSource = lst.OrderBy(Function(x) x.GetType().GetProperty(_strSortColumn01).GetValue(x)). _
                                ThenBy(Function(x) x.GetType().GetProperty(_strSortcolumn02).GetValue(x)).ToList()
        Else
            Me.DataSource = lst.OrderByDescending(Function(x) x.GetType().GetProperty(_strSortColumn01).GetValue(x)). _
                                          ThenByDescending(Function(x) x.GetType().GetProperty(_strSortcolumn02).GetValue(x)).ToList()
        End If
    End If
End Sub

最后一个If Block是给定静态类型的List(Of T)时起作用的代码。 但是,当尝试动态确定类型时,此代码将失败。 鉴于在运行时lst变量是正确的类型,它以相当令人沮丧的方式执行此操作,但是在OrderBy上出现错误:

Microsoft.VisualBasic.dll中发生了类型为'System.MissingMemberException'的第一次机会异常

其他信息:找不到类型为“ List(Of CustomerInquiryGridBE)”的公共成员“ OrderBy”。

重要的是要注意List(Of CustomerInquiryGridBE)是数据源的实际类型,当明确提供该类型时,此完全相同的代码也可以正常工作。 反正有做这项工作吗?

首先,我必须说,我找到了一种方法,用于直接连接到数据库的应用程序的Dataset> DataTable。
正如我们从MSDN所知道的:

 *To support sorting, the underlying list must implement the IBindingList or IBindingListView, interfaces. This capability can be queried through the SupportsSorting property. Multicolumn sorting is available when the SupportsAdvancedSorting property is true.
Setting the Sort property will change the internal list depending on its type:
If the list is of type IBindingList, the IBindingList.SortProperty and IBindingList.SortDirection properties are set in the internal list.
If the list is of type IBindingListView, the IBindingListView.SortDescriptions property is set.*

但是我有一个使用MVP模式和EF6的Winform应用程序
在我的演示者中,我已经有了List(Of T),并且我不想更改工作代码。
我从MSDN的C#示例中手动转换了这个类,在我的情况下有效!
首先在您的项目中导入此类:SortableBindingList

 Imports System.ComponentModel
 Imports System.Reflection

 Namespace Repository

Public Class SortableBindingList(Of T)
    Inherits BindingList(Of T)
    Private sortedList As ArrayList
    Private unsortedItems As ArrayList
    Private isSortedValue As Boolean

    Public Sub New()
    End Sub

    Public Sub New(list As IList(Of T))
        For Each o As Object In list
            Me.Add(DirectCast(o, T))
        Next
    End Sub

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

    Protected Overrides Function FindCore(prop As PropertyDescriptor, key As Object) As Integer
        Dim propInfo As PropertyInfo = GetType(T).GetProperty(prop.Name)
        Dim item As T

        If Not (key Is Nothing) Then
            Dim i As Integer = 0
            While i < Count
                item = DirectCast(Items(i), T)
                If propInfo.GetValue(item, Nothing).Equals(key) Then
                    Return i
                End If
                System.Threading.Interlocked.Increment(i)
            End While
        End If
        Return -1
    End Function

    Public Function Find([property] As String, key As Object) As Integer
        Dim properties As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(T))
        Dim prop As PropertyDescriptor = properties.Find([property], True)

        If IsNothing(prop) Then
            Return -1
        Else
            Return FindCore(prop, key)
        End If
    End Function

    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

    Private sortDirectionValue As ListSortDirection
    Private sortPropertyValue As PropertyDescriptor

    Protected Overrides Sub ApplySortCore(prop As PropertyDescriptor, direction As ListSortDirection)
        sortedList = New ArrayList()

        '// Check to see if the property type we are sorting by implements
        '// the IComparable interface.
        Dim interfaceType As Type = prop.PropertyType.GetInterface("IComparable")

        If interfaceType = Nothing AndAlso prop.PropertyType.IsValueType Then
            Dim underlyingType As Type = Nullable.GetUnderlyingType(prop.PropertyType)

            If Not (underlyingType Is Nothing) Then
                interfaceType = underlyingType.GetInterface("IComparable")
            End If
        End If

        If Not (interfaceType Is Nothing) Then
            '// If so, set the SortPropertyValue and SortDirectionValue.
            sortPropertyValue = prop
            sortDirectionValue = direction

            Dim query As IEnumerable(Of T) = MyBase.Items
            If direction = ListSortDirection.Ascending Then
                query = query.OrderBy(Function(i) prop.GetValue(i))
            Else
                query = query.OrderByDescending(Function(i) prop.GetValue(i))
            End If
            Dim newIndex As Integer = 0
            For Each item As Object In query
                Me.Items(newIndex) = DirectCast(item, T)
                System.Math.Max(System.Threading.Interlocked.Increment(newIndex), newIndex - 1)
            Next
            isSortedValue = True

            Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
        Else
            Throw New NotSupportedException("Cannot sort by " + prop.Name + ". This" + prop.PropertyType.ToString() + " does not implement IComparable")
        End If
    End Sub

    Protected Overrides Sub RemoveSortCore()
        Dim position As Integer
        Dim temp As Object

        If Not (unsortedItems Is Nothing) Then
            Dim i As Integer = 0
            While i < unsortedItems.Count
                position = Me.Find("LastName", unsortedItems(i).[GetType]().GetProperty("LastName").GetValue(unsortedItems(i), Nothing))
                If position > 0 AndAlso position <> i Then
                    temp = Me(i)
                    Me(i) = Me(position)
                    Me(position) = DirectCast(temp, T)
                    System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
                ElseIf position = i Then
                    System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
                Else
                    unsortedItems.RemoveAt(i)
                End If
            End While
            isSortedValue = False
            OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
        End If
    End Sub

    Public Sub RemoveSort()
        RemoveSortCore()
    End Sub
    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

    Public Sub myPublicSort(ByRef prop As PropertyDescriptor, direction As ListSortDirection)
        ApplySortCore(prop, direction)
    End Sub

 End Class
 End Namespace

将Namspace更改为您的!

然后在我的表单中Load()
在这里,我将我的初始List(Of T)转换为sortableBindingList(Of T)
将此SortableBindingList分配为BindingSource的DataSource,不要忘记将DataGridView DataSource设置为YourBindingSource(在我的情况下为PatientBindingSource)

 For Each item As Patient In _presenter.GetAllPatient()  ' my List(Of Patients)
        sortableBList.Add(item)
 Next

    Try
        PatientBindingSource.DataSource = sortableBList                              '_presenter.listaPacienti
    Catch ex As ArgumentException
        Console.WriteLine(ex)
    End Try
    PatientBindingSource.ResetBindings(False)

之后,添加此

  'this is for Programatic Sort
  Private Sub dgvPacient_DataBindingComplete(sender As Object, e As DataGridViewBindingCompleteEventArgs) Handles dgvPacient.DataBindingComplete
    For Each Column As DataGridViewColumn In dgvPacient.Columns
        Column.SortMode = DataGridViewColumnSortMode.Programmatic
    Next Column
End Sub


'click on Column Header to sort
Private Sub dgvPacient_ColumnHeaderMouseClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles dgvPacient.ColumnHeaderMouseClick
    If isInitDone Then
        Try
            Debug.WriteLine("click pe header !")

            Dim ColumnNameBruteFromDGV As String = dgvPacient.Columns(e.ColumnIndex).Name
            Dim ColumnName As String = ColumnNameBruteFromDGV.Replace("DataGridViewTextBoxColumn", "")
            Dim lastColumnDir As String = ""
            Dim SortDirection As ListSortDirection

            If IsNothing(PatientBindingSource.Sort) Then
                SortDirection = ListSortDirection.Ascending
                PatientBindingSource.Sort = "LastName" & " ASC"
            Else
                If PatientBindingSource.Sort.Contains(lastColumnName) Then
                    If PatientBindingSource.Sort.Contains("DESC") Then
                        PatientBindingSource.Sort = lastColumnName & lastColumnDir & " " & ColumnName & " ASC"
                        SortDirection = ListSortDirection.Ascending
                    Else
                        PatientBindingSource.Sort = lastColumnName & lastColumnDir & " " & ColumnName & " DESC"
                        SortDirection = ListSortDirection.Descending
                    End If
                Else
                    If PatientBindingSource.Sort.Contains("DESC") Then
                        lastColumnName = ColumnName
                        lastColumnDir = " DESC"
                        PatientBindingSource.Sort = lastColumnName & lastColumnDir
                        SortDirection = ListSortDirection.Descending
                    Else
                        lastColumnName = ColumnName
                        lastColumnDir = " ASC"
                        PatientBindingSource.Sort = lastColumnName & lastColumnDir
                        SortDirection = ListSortDirection.Ascending
                    End If
                End If

            End If

            ' ColumnName

            dgvPacient.Columns(e.ColumnIndex).HeaderCell.SortGlyphDirection = _
                If(SortDirection = ListSortDirection.Ascending, _
                   SortOrder.Ascending, _
                   SortOrder.Descending _
            )

        Catch ex As ArgumentException
            Debug.WriteLine("dgvPacient_ColumnHeaderMouseClick Exception : " & ex.Message)
        End Try
    End If
End Sub

用您的BindingSourceName更改或替换PatientBindingSource
并将dgvPacient替换为yourDataGridViewName ..
我有点头疼,现在没有时间了,有了HeaderCell.SortGlyphDirection,如果我单击一个colun标头,进行排序,然后单击其他列,然后按2列进行排序,但是现在我在最后一列上有GlyphDirection ..
快乐的外衣:)
克里斯蒂C777

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM