Imports System.ComponentModel ''' ''' ''' Namespace Core ''' ''' This class implements sorting functionality for collections. ''' ''' ''' ''' This class inhirits from Core.BindableCollectionBase and adds ''' sorting capability to collections. BusinessCollectionBase inherits ''' from this class, and business collections should inherit from ''' BusinessCollectionBase. Core.SortedCollectionBase is for internal ''' framework use only. ''' ''' The Core.BindableCollectionBase class implements the IBindableList ''' interface. However, it doesn't actually implement sorting. Instead ''' it delegates the sorting functionality to a set of protected virtual ''' methods. This class provides the actual sorting implementation ''' by overriding those methods. ''' ''' _ Public Class SortableCollectionBase Inherits BindableCollectionBase _ Private mIsSorted As Boolean = False _ Private mSortProperty As PropertyDescriptor _ Private mSortPropertyName As String _ Private mListSortDirection As ListSortDirection = ListSortDirection.Ascending _ Private mUnsortedList As ArrayList _ Private mActivelySorting As Boolean = False #Region " Properties " ''' ''' Indicates whether the collection is in the process of ''' being sorted at this time. ''' Protected ReadOnly Property ActivelySorting() As Boolean Get Return mActivelySorting End Get End Property ''' ''' Returns a value indicating whether the collection is currently ''' sorted. ''' Protected Overrides ReadOnly Property IBindingList_IsSorted() As Boolean Get Return mIsSorted End Get End Property ''' ''' Returns the property by which the collection is currently sorted. ''' ''' ''' This method is invoked via the IBindingList interface and is not ''' intended for use by code in your business class. ''' Protected Overrides ReadOnly Property IBindingList_SortProperty() As PropertyDescriptor Get If mSortProperty Is Nothing AndAlso Len(mSortPropertyName) > 0 Then Try ' we need to recreate the sortproperty value Dim childType As Type If list.Count > 0 Then ' get child type from the first element in the collection childType = list.Item(0).GetType Else ' get child type from Item property Try childType = Me.GetType.GetProperty("Item", New Type() {GetType(Integer)}).PropertyType Catch childType = GetType(Object) End Try End If ' now get the property descriptor from the type mSortProperty = _ TypeDescriptor.GetProperties(childType).Item(mSortPropertyName) Catch ' we failed to recreate it - return nothing mSortProperty = Nothing End Try End If Return mSortProperty End Get End Property ''' ''' Returns the current sort direction. ''' ''' ''' This method is invoked via the IBindingList interface and is not ''' intended for use by code in your business class. ''' Protected Overrides ReadOnly Property IBindingList_SortDirection() As ListSortDirection Get Return mListSortDirection End Get End Property #End Region #Region " ApplySort " ''' ''' Structure to store temporary data for sorting. ''' Private Structure SortData Private mKey As Object Public Value As Object Public Sub New(ByVal Key As Object, ByVal Value As Object) mKey = Key Me.Value = Value End Sub Public ReadOnly Property Key() As Object Get If IsNumeric(mKey) OrElse TypeOf mKey Is String Then Return mKey Else Return mKey.ToString End If End Get End Property End Structure ''' ''' Contains code to compare SortData structures ''' ''' ''' This performs a case sensitive comparison. If you want a case insensitive ''' comparison, change the code to use CaseInsensitiveComparer.Default instead. ''' Private Class SortDataCompare Implements IComparer Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare Dim item1 As SortData = DirectCast(x, SortData) Dim item2 As SortData = DirectCast(y, SortData) Return Comparer.Default.Compare(item1.Key, item2.Key) End Function End Class ''' ''' Applies a sort to the collection. ''' ''' ''' This method is invoked via the IBindingList interface and is not ''' intended for use by code in your business class. ''' Protected Overrides Sub IBindingList_ApplySort(ByVal [property] As System.ComponentModel.PropertyDescriptor, ByVal direction As System.ComponentModel.ListSortDirection) If Not AllowSort Then Throw New NotSupportedException("Sorting is not supported by this collection.") End If mSortProperty = [property] mSortPropertyName = mSortProperty.Name mListSortDirection = direction If Not mIsSorted AndAlso list.Count > 0 Then ' this is our first time sorting so ' make sure to store the original order mUnsortedList = New ArrayList() Dim item As Object For Each item In list mUnsortedList.Add(item) Next End If If list.Count > 1 Then Try Dim count As Integer mActivelySorting = True ' copy the key/value pairs into a sorted list Dim sortList As New ArrayList() For count = 0 To list.Count - 1 sortList.Add(New SortData(CallByName(list.Item(count), mSortPropertyName, CallType.Get), list.Item(count))) Next sortList.Sort(New SortDataCompare()) list.Clear() If direction = ListSortDirection.Ascending Then Dim item As SortData For Each item In sortList list.Add(item.Value) Next Else ' direction = ListSortDirection.Descending Dim item As SortData For count = sortList.Count - 1 To 0 Step -1 item = DirectCast(sortList(count), SortData) list.Add(item.value) Next End If mIsSorted = True Catch IBindingList_RemoveSort() Finally mActivelySorting = False End Try ElseIf list.Count = 1 Then mIsSorted = True End If End Sub #End Region #Region " RemoveSort " ''' ''' Removes any sort from the collection. ''' ''' ''' This method is invoked via the IBindingList interface and is not ''' intended for use by code in your business class. ''' Protected Overrides Sub IBindingList_RemoveSort() If Not AllowSort Then Throw New NotSupportedException("Sorting is not supported by this collection.") End If If mIsSorted Then mActivelySorting = True 'Return the list to its unsorted state list.Clear() Dim item As Object For Each item In mUnsortedList list.Add(item) Next mUnsortedList = Nothing mIsSorted = False mSortProperty = Nothing mSortPropertyName = "" mListSortDirection = ListSortDirection.Ascending mActivelySorting = False End If End Sub #End Region #Region " Collection events " ''' ''' Ensures that any sort is maintained as a new item is inserted. ''' Protected Overrides Sub OnInsertComplete(ByVal index As Integer, ByVal value As Object) If mIsSorted AndAlso Not ActivelySorting Then mUnsortedList.Add(value) End If MyBase.OnInsertComplete(index, value) End Sub ''' ''' Ensures that any sort is maintained as the list is cleared. ''' Protected Overrides Sub OnClearComplete() If mIsSorted AndAlso Not ActivelySorting Then mUnsortedList.Clear() End If MyBase.OnClearComplete() End Sub ''' ''' Ensures that any sort is maintained as an item is removed. ''' Protected Overrides Sub OnRemoveComplete(ByVal index As Integer, ByVal value As Object) If mIsSorted AndAlso Not ActivelySorting Then mUnsortedList.Remove(value) End If MyBase.OnRemoveComplete(index, value) End Sub #End Region #Region " Search/Find " ''' ''' Implements search/find functionality for the collection. ''' Protected Overrides Function IBindingList_Find(ByVal [property] As PropertyDescriptor, ByVal key As Object) As Integer If Not AllowFind Then Throw New NotSupportedException("Searching is not supported by this collection.") End If Dim index As Integer Dim tmp As Object Dim prop As String = [property].Name For index = 0 To list.Count - 1 tmp = CallByName(list.Item(index), prop, CallType.Get) If tmp.Equals(key) Then ' we found a match Return index End If Next ' we didn't find anything Return -1 End Function #End Region End Class End Namespace