2015-04-28 7 views
0

У нас есть пользовательская реализация DataGridView (она мало что делает, просто обрабатывает форматирование и т. П.). Все элементы управления DataGridView привязаны к списку пользовательских объектов (System.Generic.List(Of MyObjectName)). Мне было поручено сделать все эти сетки отсортированными по нескольким столбцам (я начну с двух, и при необходимости измените их позже).Динамическая сортировка нескольких столбцов в DataGridView

В настоящее время сортировка этих сеток обрабатывается путем вызова метода преобразования списка (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 

Последнее Если блок является код, который работает, когда данный статически типизированный список (Of T). Однако при попытке динамического определения типа этот код терпит неудачу. Он делает это в довольно расстраивать образом, учитывая, что во время выполнения переменная lst правильный тип, но я получаю сообщение об ошибке на OrderBy:

первого шанса исключение типа «System.MissingMemberException» произошло в Microsoft.VisualBasic.dll

Дополнительная информация: Публичный участник 'OrderBy' on type 'List (Of CustomerInquiryGridBE)' не найден.

Важно отметить, что List(Of CustomerInquiryGridBE) является фактическим типом DataSource, и это тот же самый код работает нормально, когда тип явно предусмотрен. Есть ли способ сделать эту работу?

+0

http://www.codeproject.com/Articles/19867/How- To-Allow-To-Sort-By-Multiple-Columns-in-Custom – CristiC777

+0

BindingSource.Sort = "Column1 ASC, Column2 DESC, Column 3 ASC"; – CristiC777

+0

@ CristiC777 Я также попробовал BindingSource. Это не ошибка, но она тоже ничего не сделала. –

ответ

0

Прежде всего, я должен сказать, что нашел способ сделать с 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.* 

Но у меня есть приложение Winform с помощью MVP модели с EF6
В моем предъявитель у меня уже был список (Of T), и я не хочу, чтобы изменить рабочий код.
Этот класс I, преобразованный вручную из примера на C# из MSDN, в моем случае работает!
Первый InPort это classs в проекте: 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()
Здесь я преобразовал свой первоначальный список (Of T) для sortableBindingList (Of T)
Назначьте этот SortableBindingList как DataSource из BindingSource, не забудьте установить 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 

изменить или заменить PatientBindingSource с BindingSourceName
и заменить dgvPacient с yourDataGridViewName ..
У меня есть немного issuse, что у меня нет времени сейчас, с HeaderCell.SortGlyphDirection, если я нажимаю на заголовке Colun, сортируется, и нажмите на другой колонке, и сортируется по 2 колонки, но теперь у меня есть GlyphDirection только на последней колонке ..
Happy Codding :)
CristiC777

+0

Надеюсь, вы узнаете, как это решить :) – CristiC777