Wednesday, February 4, 2009

How to make binding to objects (classes) work

Problem: From my service I receive a class collection and when showing data in datagridview, there is no sorting posible, bindingsource functions like addnew do not work etc. After I have spended quite some time online to find an apropriate solution, i finally managed to produce staisfactory solution: Solution: first make a standard class that contains data.


Public  Class ListOfUnits
  Private  _ID As Integer
Public Property ID() As Integer
    Get
       Return _ID
    End Get
  Set(ByVal value As  Integer)
      _ID = value
   End Set
End Property
Private _UNIT As String
Public  Property UNIT() As String
  Get
      Return _UNIT
  End Get
  Set(ByVal value As String)
      _UNIT= value
  End Set
End Property
end  class

then make another class, that inherits from bindinglist(of T), where T is your class name


Public Class SortableListOfUnits
  Inherits BindingList(Of ListOfUnits)

  Private mIsSorted As Boolean

  Private m_SortDirection As ListSortDirection
  Private m_SortProperty As PropertyDescriptor
  Protected Overrides ReadOnly Property SortPropertyCore() As System.ComponentModel.PropertyDescriptor
    Get
      Return m_SortProperty
    End Get
  End Property
  Protected Overrides ReadOnly Property SortDirectionCore() As System.ComponentModel.ListSortDirection
    Get
      Return m_SortDirection
    End Get
  End Property
  Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
    Get
      Return True
    End Get
  End Property

  Protected Overrides Sub ApplySortCore(ByVal prop As System.ComponentModel.PropertyDescriptor, ByVal direction As System.ComponentModel.ListSortDirection)
    Dim items As System.Collections.Generic.List(Of ListOfUnits) = DirectCast(Me.Items, List(Of ListOfUnits))
    If Not IsNothing(items) Then
      m_SortDirection = direction
      m_SortProperty = prop
      items.Sort(New PropertyComparer(Of ListOfUnits)(prop.Name, direction))
      mIsSorted = True
    Else
      mIsSorted = False
    End If
    Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
  End Sub
  Protected Overrides ReadOnly Property IsSortedCore() As Boolean
    Get
      Return mIsSorted
    End Get
  End Property
  Protected Overrides Sub RemoveSortCore()
    mIsSorted = False
  End Sub
End Class

3) all you need now is a property comparer or in simple words a class that will implement a IComparer(Of T) interface if you want to make your own comparing logic. I have found a excelent implementation of icomparer online, that is generic, since i do not want to write comparer logic for every class.


Imports System.Reflection

Public Class PropertyComparer(Of T)
  Implements IComparer(Of T)

  Private FPropertyName As String = ""
  Private FDirection As SortDirection

  Public Sub New(ByVal propertyName As String)
    FPropertyName = propertyName
    FDirection = SortDirection.Ascending
  End Sub
  Public Sub New(ByVal propertyName As String, ByVal Direction As SortDirection)
    FPropertyName = propertyName
    FDirection = Direction
  End Sub
  ' Try to sort based on type using CompareTo method
  ' Multiple by FDirection to alternate sort direction
  Public Function Compare(ByVal x As T, ByVal y As T) _
     As Integer Implements System.Collections.Generic. _
        IComparer(Of T).Compare
    Dim propertyX As PropertyInfo = x.GetType().GetProperty(FPropertyName)
    Dim propertyY As PropertyInfo = y.GetType().GetProperty(FPropertyName)
    Dim px As Object = propertyX.GetValue(x, Nothing)
    Dim py As Object = propertyY.GetValue(y, Nothing)
    If (TypeOf px Is Integer) Then
      Return Compare(Of Integer)(CType(px, Integer), CType(py, Integer)) * FDirection
    End If
    If (TypeOf px Is Decimal) Then
      Return Compare(Of Decimal)(CType(px, Decimal), CType(py, Decimal)) * FDirection
    End If
    If (TypeOf px Is DateTime) Then
      Return Compare(Of DateTime)(CType(px, DateTime), CType(py, DateTime)) * FDirection
    End If
    If (TypeOf px Is Double) Then
      Return Compare(Of Double)(CType(px, Double), CType(py, Double)) * FDirection
    End If
    If (TypeOf px Is String) Then
      Return Compare(Of String)(CType(px, String), CType(py, String)) * FDirection
    End If
    If (TypeOf px Is Decimal) Then
      Return Compare(Of Decimal)(CType(px, Decimal), CType(py, Decimal)) * FDirection
    End If
    Dim methodX As MethodInfo = propertyX.GetType().GetMethod("CompareTo")
    If (methodX Is Nothing = False) Then
      Return CType(methodX.Invoke(px, New Object() {py}), _
         Integer) * FDirection
    Else
      Return 0
    End If
  End Function
  Private Function Compare(Of K As IComparable)(ByVal x As K, _
     ByVal y As K) As Integer
    Return x.CompareTo(y)
  End Function


  Public Enum SortDirection
    Descending = -1
    Ascending = 1
  End Enum

End Class

Post a Comment