I never bind data controls. It gives for more control and flexibility. It is more work, but I think it is worth it, since you can copy and paste after you've created the first one (or better yet, write a code generator).

You need two things:
  1. A collection
  2. An object representing a row


You can use a generic collection, but I recommend a strongly typed collection. Here's an example: (Change EmailRecipientCollection to the collection name of your coice and EmailRecipient to your table name)
Code:
    Public Class EmailRecipientCollection
        'Strongly typed collection of rows from tblEmailRecipient

        Inherits CollectionBase


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ ReadOnly Properties ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

        Public Shadows ReadOnly Property Count() As Integer

            Get
                Return MyBase.List.Count
            End Get

        End Property

#End Region


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ Properties ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

        Default Public Property Item(ByVal index As Integer) As EmailRecipient

            Get
                If index < MyBase.List.Count Then
                    Return CType(MyBase.List.Item(index), EmailRecipient)
                Else
                    Return Nothing
                End If
            End Get

            Set(ByVal value As EmailRecipient)
                MyBase.List.Item(index) = value
            End Set

        End Property

        Default Public Property Item(ByVal strEmailTypeID As String, ByVal strEmployeeID As String, Optional ByVal bIgnoreCase As Boolean = False) As EmailRecipient

            Get

                Dim bFound As Boolean
                Dim EmailRecipient As EmailRecipient


                If bIgnoreCase Then
                    strEmailTypeID = UCase(strEmailTypeID)
                    strEmployeeID = UCase(strEmployeeID)
                End If
                EmailRecipient = Nothing
                For Each EmailRecipient In MyBase.List
                    If bIgnoreCase Then
                        If UCase(EmailRecipient.EmailTypeID) = strEmailTypeID And UCase(EmailRecipient.EmployeeID) = strEmployeeID Then
                            bFound = True
                            Exit For
                        End If
                    Else
                        If EmailRecipient.EmailTypeID = strEmailTypeID And EmailRecipient.EmployeeID = strEmployeeID Then
                            bFound = True
                            Exit For
                        End If
                    End If
                Next
                If bFound Then
                    Return EmailRecipient
                Else
                    Return Nothing
                End If
            End Get

            Set(ByVal value As EmailRecipient)

                Dim bFound As Boolean
                Dim EmailRecipient As EmailRecipient


                If bIgnoreCase Then
                    strEmailTypeID = UCase(strEmailTypeID)
                    strEmployeeID = UCase(strEmployeeID)
                End If
                EmailRecipient = Nothing
                For Each EmailRecipient In MyBase.List
                    If bIgnoreCase Then
                        If UCase(EmailRecipient.EmailTypeID) = strEmailTypeID And UCase(EmailRecipient.EmployeeID) = strEmployeeID Then
                            bFound = True
                            Exit For
                        End If
                    Else
                        If EmailRecipient.EmailTypeID = strEmailTypeID And EmailRecipient.EmployeeID = strEmployeeID Then
                            bFound = True
                            Exit For
                        End If
                    End If
                Next
                If bFound Then
                    EmailRecipient.CopyOf(value)
                End If
            End Set

        End Property

#End Region


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ Methods ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

        Public Sub New()

            MyBase.new()

        End Sub

        Public Overrides Function ToString() As String

            Return "Collection of EmailRecipient objects, count = " & MyBase.List.Count.ToString

        End Function

        Public Function Add() As EmailRecipient

            Dim EmailRecipient As EmailRecipient


            EmailRecipient = New EmailRecipient
            MyBase.List.Add(EmailRecipient)
            Return EmailRecipient

        End Function

        Public Sub Add(ByVal EmailRecipient As EmailRecipient)

            MyBase.List.Add(EmailRecipient)

        End Sub

        Public Overloads Sub Clear()

            List.Clear()

        End Sub

        Public Function Contains(ByVal EmailRecipient As EmailRecipient) As Boolean

            Return List.Contains(EmailRecipient)

        End Function

        Public Sub CopyTo(ByVal Array() As EmailRecipient, ByVal intIndex As Integer)

            List.CopyTo(Array, intIndex)

        End Sub

        Public Sub Insert(ByVal intIndex As Integer, ByVal EmailRecipient As EmailRecipient)

            List.Insert(intIndex, EmailRecipient)

        End Sub

        Public Function IndexOf(ByVal EmailRecipient As EmailRecipient) As Integer

            Return List.IndexOf(EmailRecipient)

        End Function

        Public Sub Remove(ByVal EmailRecipient As EmailRecipient)

            MyBase.List.Remove(EmailRecipient)

        End Sub

        Public Sub Sort()

            MyBase.InnerList.Sort()

        End Sub

        Public Sub Sort(ByVal Comparer As IComparer)

            MyBase.InnerList.Sort(Comparer)

        End Sub


        Public Sub SaveList()

            Dim EmailRecipient As EmailRecipient


            For Each EmailRecipient In MyBase.List
                If EmailRecipient.Dirty Then
                    SaveEmailRecipient(EmailRecipient)
                End If
            Next

        End Sub

        Public Sub DeleteList()

            Dim EmailRecipient As EmailRecipient


            For Each EmailRecipient In MyBase.List
                DeleteEmailRecipient(EmailRecipient)
            Next

        End Sub

#End Region


    End Class 'EmailRecipientCollection
And here's an example of a row object:
Code:
        Public Class EmailRecipient
            'Represents a row from table tblEmailRecipient


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ Variables ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

            'State variables
            Private mbDirty As Boolean
            Private mbExists As Boolean
            Private mbSetFromCode As Boolean
            'Field variables
'TODO Make local variables to represent columns in your table
'Example: Private mstrEmailTypeID As String
            'Holds the last data access error
            Private mstrLastError As String
            'If Readonly is true, then no Saves or Deletes can be done
            Private mbReadonly As Boolean

#End Region


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ ReadOnly Properties ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

            Public ReadOnly Property Dirty() As Boolean Implements MainDataAccess.IDataAccess.Dirty

                Get
                    Return mbDirty
                End Get

            End Property

            Public ReadOnly Property ExistsInDB() As Boolean Implements MainDataAccess.IDataAccess.ExistsInDB

                Get
                    Return mbExists
                End Get

            End Property

            Public ReadOnly Property LastDBError() As String Implements MainDataAccess.IDataAccess.LastDBError

                Get
                    Return mstrLastError
                End Get

            End Property

            Public ReadOnly Property [Readonly]() As Boolean Implements MainDataAccess.IDataAccess.[Readonly]

                Get
                    Return mbReadonly
                End Get

            End Property

#End Region


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ Properties ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

            Public Property SetFromCode() As Boolean
                Get
                    Return mbSetFromCode
                End Get
                Set(ByVal value As Boolean)
                    mbSetFromCode = value
                End Set

            End Property


'TODO Make properties representing all columns in your table, example:
            Public Property EmailTypeID() As String
                Get
                    Return mstrEmailTypeID
                End Get
                Set(ByVal value As String)
                    If Not mbReadonly Then
                        mstrEmailTypeID = value
                        If Not SetFromCode Then
                            mbDirty = True
                        End If
                    End If
                End Set

            End Property



#End Region


#Region "¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤ Methods ¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤×¤"

            Public Sub New()

                MyBase.new()

            End Sub

            Public Sub New(ByVal bExists As Boolean, ByVal bReadonly As Boolean)

                MyBase.new()
                mbExists = bExists
                mbReadonly = bReadonly

            End Sub

            Public Function Copy() As EmailRecipient

                Return CType(Me.MemberwiseClone(), EmailRecipient)

            End Function


#End Region

        End Class 'EmailRecipient
If you wish to implement it you just need a method to fill it and a method to fill the grid. It makes an excellent layer of an n-tier system.