Article Options
Recently Viewed
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.34782608695652 out of 5
 23 people have rated this page
Article Score41350
Comments    Submit Comment

Comment #1  (Posted by Edneeis on 01/03/2003)

Here is an updated version to fix the Remove method and add a KeyOf function.

[code=vb]
Public MustInherit Class KeyedCollectionBase
Inherits System.Collections.CollectionBase
Protected keys As New SortedList()
Protected keymember As String

Dim boundgrid As System.Windows.Forms.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 Object) 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(Me.KeyOf(Item)))
keys.RemoveAt(Index)
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 KeyOf(ByVal Item As Object) As Object
Return keys.GetKeyList.Item(keys.IndexOfValue(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()
Me.keymember = KeyMember
End Sub

Protected Overrides Sub OnClearComplete()
MyBase.OnClearComplete()
If Not boundgrid Is Nothing Then RefreshGrid()

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

Protected Overrides Sub OnInsertComplete(ByVal index As Integer, ByVal value As Object)
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)
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)
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 System.Windows.Forms.DataGrid)
boundgrid = dg
End Sub

'supportive
Private Sub IncreaseIndexes(ByVal start As Integer)
'increment values of all keys after key
'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
'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()
boundgrid.DataSource = Nothing
boundgrid.DataSource = Me
End Sub

End Class

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
[/code]
 
Comment #2  (Posted by Eddie de Bear on 02/05/2003)

Just a suggestion.

You can bypass the "Typed interface" of the collection very simply.. All you need to do is use the IList interface which allows .Add, .Insert etc for type object..

This can be prevented by Overriding OnInsert and OnSet. Both of these methods allow you the opertunity of raising an exception if the incorrect datatype
is passed in through the IList interface...

It just makes the whole colelction a little safer...

Eddie de bear
MCSD
 
Comment #3  (Posted by Andrew Marshall on 02/13/2004)

Does your implementation support For Each ... Next syntax?
 
Comment #4  (Posted by Manikandan on 08/17/2004)

I was working with vb.net, i couldnt get the database conectivity & Retrive records from Textbox to access.if you have any suggestion about the coding for this part in net .please sent it to.

 
Comment #5  (Posted by an unknown user on 08/18/2005)
Rating
I have been looking for a complete implementation on CollectionBase for ages showing how it is populated then how to retrive data i.e in a datagrid. Thanks
 
Comment #6  (Posted by an unknown user on 06/02/2006)
Rating
Thanks a lot!
 
Comment #7  (Posted by an unknown user on 12/11/2007)
Rating
Exactly what I need in VB.Net for implementing custom object collections.
 
Comment #8  (Posted by Settor64 on 10/22/2009)
Rating
I probably already tried to plug here. ,
 
Comment #9  (Posted by an unknown user on 12/10/2009)
Rating
frfgdfffdfgdef
 
Comment #10  (Posted by buy cheap oem software on 02/12/2012)
Rating
zuoie3 As usual, the webmaster posted correctly..!!
 
Comment #11  (Posted by esalerugs on 02/13/2012)
Rating
Yet, much is unclear. Could you describe in more details!....
 
Comment #12  (Posted by cheapostay promo code on 02/13/2012)
Rating
Right from this article begin to read this blog. Plus a subscriber:DD
 
Comment #13  (Posted by How to get free followers on 02/13/2012)
Rating
Can be also this issue because the truth can be achieved only in a dispute :DD
 
Comment #14  (Posted by viagra on 02/13/2012)
Rating
Current blog, fresh information, I read it from time to time!!....
 
Comment #15  (Posted by Marlton NJ personal trainer on 02/13/2012)
Rating
Current blog, fresh information, I read it from time to time!!....
 
Sponsored Links