Article Options
Premium Sponsor
Premium Sponsor

 »  Home  »  .NET Framework  »  Type Safe Collection implementation in VB.NET
Type Safe Collection implementation in VB.NET
by Guest Author | Published  12/15/2002 | .NET Framework | Rating:
Guest Author
This author account is for guest publications only, and does not reflect the bio for any particular author. 

View all articles by Guest Author...
Type Safe Collection implementation in VB.NET

Guest Author: Ed Marquez

This is my base for Type Safe Collections. It uses the CollectionBase but then adds the use of object keys internally, a CountChange event, and an easy system to make sure a DataGrid bound to it updates itself on changes. To use the class just inherit from it and add Shadowed version of the methods that take an object parameter, replacing the object type with the type safe object and then internally just call the NON type safe version of the method to carry out the work. All the Protected methods are the ones that should be replaced.

Public MustInherit Class KeyedCollectionBase
    '''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Author: Ed Marquez
    ' Description: This is my base for type safe collections. It uses the collectionbase but then adds
    ' the use of object keys internally, a countchange event, and an easy system to make
    ' sure a datagrid bound to it updates itself on changes. To use the class just inherit
    ' from it and add Shadowed version of the methods that take an object parameter, replacing
    ' the object type with the type safe object and then internally just call the NON type safe
    ' version of the method to carry out the work. All the Protected methods are the ones that
    ' should be replaced.
    '''''''''''''''''''''''''''''''''''''''''''''''''''
    Inherits System.Collections.CollectionBase
    Protected keys As New SortedList()
    Protected keymember As String
    Private boundgrid As DataGrid

    Public Event CountChange(ByVal sender As Object, ByVal e As CountEventArgs)

    Protected Function Add(ByVal Items As KeyedCollectionBase) As Integer()
        Dim al As New ArrayList()
        Dim itm As Object
        For Each itm In Items
            Dim key As Object = itm.GetType.GetProperty(keymember).GetValue(itm, Nothing)
            keys.Add(key, MyBase.List.Add(itm))
            al.Add(keys(key))
        Next
        Return al.ToArray(GetType(Integer))
    End Function

    Protected Function Add(ByVal Items() As Object) As Integer()
        Dim al As New ArrayList()
        Dim itm As Object
        For Each itm In Items
            Dim key As Object = itm.GetType.GetProperty(keymember).GetValue(itm, Nothing)
            keys.Add(key, MyBase.List.Add(itm))
            al.Add(keys(key))
        Next
        Return al.ToArray(GetType(Integer))
    End Function

    Protected Function Add(ByVal Item As Object) As Integer
        Dim key As Object = Item.GetType.GetProperty(keymember).GetValue(Item, Nothing)
        keys.Add(key, MyBase.List.Add(Item))
        Return keys(key)
    End Function

    Protected Function Add(ByVal Item As Object, ByVal Key As Object) As Integer
        keys.Add(Key, MyBase.List.Add(Item))
        Return keys(Key)
    End Function

    Default Protected ReadOnly Property Item(ByVal Key As String) As Object
        Get
            Return MyBase.List.Item(keys(Key))
        End Get
    End Property

    Default Protected ReadOnly Property Item(ByVal Index As Integer) As Object
        Get
            Return MyBase.List.Item(Index)
        End Get
    End Property

    Protected Sub Remove(ByVal Item As Object)
        Dim Index As Integer = keys.IndexOfValue(Me.IndexOf(Item))
        keys.RemoveAt(keys.IndexOfValue(Me.IndexOf(Item)))
        MyBase.List.Remove(Item)
        DecreaseIndexes(Index)
    End Sub

    Public Shadows Sub RemoveAt(ByVal Key As Object)
        Dim Index As Integer = keys(Key)
        keys.Remove(Key)
        MyBase.List.RemoveAt(Index)
        DecreaseIndexes(Index)
    End Sub

    Public Shadows Sub RemoveAt(ByVal Index As Integer)
        keys.RemoveAt(keys.IndexOfValue(Index))
        MyBase.List.RemoveAt(Index)
        DecreaseIndexes(Index)
    End Sub

    Public Function IndexOf(ByVal Key As Object) As Integer
        Return MyBase.List.IndexOf(MyBase.List.Item(keys(Key)))
    End Function

    Public Function IndexOf(ByVal Item As Integer) As Integer
        MyBase.List.IndexOf(Item)
    End Function

    Protected Sub Insert(ByVal Index As Integer, ByVal Item As Object)
        Dim key As Object = Item.GetType.GetProperty(keymember).GetValue(Item, Nothing)
        IncreaseIndexes(Index)
        keys.Add(key, Index)
        MyBase.List.Insert(Index, Item)
    End Sub

    Protected Sub Insert(ByVal Index As Integer, ByVal Item As Object, ByVal Key As Object)
        IncreaseIndexes(Index)
        keys.Add(Key, Index)
        MyBase.List.Insert(Index, Item)
    End Sub

    Protected Function Contains(ByVal Item As Object) As Boolean
        Return MyBase.List.Contains(Item)
    End Function

    Public Sub New(ByVal KeyMember As String)
        MyBase.New()
        'this allows you to pass in the default property of the object to use
        'as a key when no key is passed in. To not use this feature then pass in
        'Nothing
        Me.keymember = KeyMember
    End Sub

    Protected Overrides Sub OnClearComplete()
        'used to refresh a datagrid
        MyBase.OnClearComplete()
        If Not boundgrid Is Nothing Then RefreshGrid()

        'raise count change event
        RaiseEvent CountChange(Me, New CountEventArgs(CountEventArgs.ChangeTypes.Cleared))
    End Sub

    Protected Overrides Sub OnInsertComplete(ByVal index As Integer, ByVal value As Object)
        'see OnClearComplete for comments
        MyBase.OnInsertComplete(index, value)
        If Not boundgrid Is Nothing Then RefreshGrid()

        RaiseEvent CountChange(Me, New CountEventArgs(CountEventArgs.ChangeTypes.Increased, value))
    End Sub

    Protected Overrides Sub OnRemoveComplete(ByVal index As Integer, ByVal value As Object)
        'see OnClearComplete for comments
        MyBase.OnRemoveComplete(index, value)
        If Not boundgrid Is Nothing Then RefreshGrid()

        RaiseEvent CountChange(Me, New CountEventArgs(CountEventArgs.ChangeTypes.Decreased, value))
    End Sub

    Protected Overrides Sub OnSetComplete(ByVal index As Integer, ByVal oldValue As Object, ByVal newValue As Object)
        'see OnClearComplete for comments
        If Not boundgrid Is Nothing Then RefreshGrid()
    End Sub

    Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer)
        MyBase.InnerList.CopyTo(array, index)
    End Sub

    Public Sub BoundDataGrid(ByVal dg As DataGrid)
        'set to nothing if the object is no longer bound
        'see refreshgrid for comments
        boundgrid = dg
    End Sub

    'supportive
    Private Sub IncreaseIndexes(ByVal start As Integer)
        'increment values of all indexes after start
        'the internal list updates all the indexes on a removeat,remove or insert
        'but in the sorted list the the index is stored as a value and thus
        'doesn't get updated automatically this fixes that
        'call BEFORE the insert
        Dim values As IList = keys.GetValueList
        Dim cnt As Integer
        Dim cntMax As Integer = values.Count - 1
        For cnt = cntMax To 0 Step -1
            If values(cnt) >= start Then
                keys.SetByIndex(keys.IndexOfValue(values(cnt)), values(cnt) + 1)
            End If
        Next
    End Sub

    Private Sub DecreaseIndexes(ByVal start As Integer)
        'decrement values of all keys after key
        'the internal list updates all the indexes on a removeat,remove or insert
        'but in the sorted list the the index is stored as a value and thus
        'doesn't get updated automatically this fixes that
        'call AFTER the index has been removed
        Dim values As IList = keys.GetValueList
        Dim cnt As Integer
        Dim cntMax As Integer = values.Count - 1
        For cnt = cntMax To 0 Step -1
            If values(cnt) >= start Then
                keys.SetByIndex(keys.IndexOfValue(values(cnt)), values(cnt) - 1)
            End If
        Next
    End Sub

    Private Sub RefreshGrid()
        'a datagrid doesn't refresh or gives errors when bound to a collection or array
        'when items are either added or removed but if you bind to nothing then to the source again
        'it solves the problem
        boundgrid.DataSource = Nothing
        boundgrid.DataSource = Me
    End Sub

    'Common CountEventArgs
    Public Class CountEventArgs
        'Purpose: Common count change change event arguments for collections.
        Inherits EventArgs

        Public Enum ChangeTypes
            [Increased]
            [Decreased]
            [Cleared]
        End Enum

        Private _ChangeType As ChangeTypes
        Private _Item As Object

        Public Property ChangeType() As ChangeTypes
            Get
                Return _ChangeType
            End Get
            Set(ByVal Value As ChangeTypes)
                _ChangeType = Value
            End Set
        End Property

        Public Property Item() As Object
            Get
                Return _Item
            End Get
            Set(ByVal Value As Object)
                _Item = Value
            End Set
        End Property

        Public Sub New(ByVal changetype As ChangeTypes)
            Me.New(changetype, Nothing)
        End Sub

        Public Sub New(ByVal changetype As ChangeTypes, ByVal item As Object)
            MyBase.new()
            _ChangeType = changetype
            _Item = item
        End Sub

    End Class

End Class

Example of Type Safe Collection


'object for example of type safe collection
Public Class TestObject
    Private _Name As String
    Private _Address As String
    Private _Phone As String

    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal Value As String)
            _Name = Value
        End Set
    End Property

    Public Property Address() As String
        Get
            Return _Address
        End Get
        Set(ByVal Value As String)
            _Address = Value
        End Set
    End Property

    Public Property Phone() As String
        Get
            Return _Phone
        End Get
        Set(ByVal Value As String)
            _Phone = Value
        End Set
    End Property

    Public Sub New()
        MyBase.new()
    End Sub

    Public Sub New(ByVal name As String, ByVal address As String, ByVal phone As String)
        _Name = name
        _Address = address
        _Phone = phone
    End Sub

End Class

'example of use collection
Public Class TestCollection
    Inherits KeyedCollectionBase

    Public Shadows Function Add(ByVal Item As TestObject) As Integer
        Return MyBase.Add(Item)
    End Function

    Public Shadows Function Add(ByVal Item As TestObject, ByVal Key As Object) As Integer
        Return MyBase.Add(Item, Key)
    End Function

    Default Public Shadows ReadOnly Property Item(ByVal Key As String) As TestObject
        Get
            Return MyBase.Item(Key)
        End Get
    End Property

    Default Public Shadows ReadOnly Property Item(ByVal Index As Integer) As TestObject
        Get
            Return MyBase.Item(Index)
        End Get
    End Property

    Public Shadows Sub Remove(ByVal Item As TestObject)
        MyBase.Remove(Item)
    End Sub

    Public Shadows Sub Insert(ByVal Key As Object, ByVal Item As TestObject)
        MyBase.Insert(Key, Item)
    End Sub

    Public Shadows Sub Insert(ByVal Index As Integer, ByVal Item As TestObject)
        MyBase.Insert(Index, Item)
    End Sub

    Public Shadows Function Contains(ByVal Item As TestObject) As Boolean
        Return MyBase.Contains(Item)
    End Function

    Public Sub New()
        MyBase.new("Name")
    End Sub

    Public Sub New(ByVal KeyMember As String)
        MyBase.New(KeyMember)
    End Sub

End Class

How would you rate the quality of this article?
1 2 3 4 5
Poor Excellent
Tell us why you rated this way (optional):

Article Rating
The average rating is: No-one else has rated this article yet.

Article rating:3.20689655172412 out of 5
 29 people have rated this page
Article Score44025
Sponsored Links