?
Solved

Problem implementing data binding with DataGridView

Posted on 2007-08-01
8
Medium Priority
?
499 Views
Last Modified: 2012-08-14
Hi guys,
I thought I had this issue covered but it seems not. I have a data bound DataGridView to a IList (Of T) collection. I can make changes to data (either in DataGridView or the object programatically) but I can't work ot how to insert/delete rows/objects programatically. If I remove an object the DataGridView gives me erors as it has an extra row and if I delete a DataGridView row first then I get objct index errors.
My understanding is that providing I implement IList (of T) interface then my collection and DataGridView should be able to synchronise automatially.

To help me resolve the problem I went back to a (very simple) sample that I used in he past with the aim of getting the mechanism working and then I would just simply adjust my project code accordingly.

Below is the whole code of my sample. If you want to run the sample just create a Form with a DataGridView called (grdCustomers) and two command buttons (btnAddRow and btnDeleteRow), that I used to Add and Delete a row.

ANy assistance would be much appreciated.

/Michael

'------------------------------------------------------------------------------------------------
Imports System.ComponentModel           ' Needed for using <Bindable(True)>


Public Class frmCustomers

    Dim Customers As New CustomersList

    Private Sub frmCustomers_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        MyTrace("+frmCust:LoadCustomers")
        Try
            AddHandler Customers.MyTrace, AddressOf MyTrace
            Dim l As IList = CType(Customers, IList)
            l.Add(ReadCustomer(1))
            l.Add(ReadCustomer(2))
            l.Add(ReadCustomer(3))
            l.Add(ReadCustomer(4))
            Me.grdCustomers.DataSource = Customers
            Me.grdCustomers.Refresh()
            MyTrace("-frmCust:LoadCustomers")
        Catch ex As Exception
            MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
        End Try
    End Sub

    Private Function ReadCustomer(ByVal n As Int16) As Customer
        MyTrace("+CList:ReadCustomer")
        Dim cust As New Customer(Customers)
        cust.FirstName = "First" & n
        cust.LastName = "Last" & n
        MyTrace("-CList:ReadCustomer`")
        Return cust
    End Function

    Private Sub MyTrace(ByVal Mess As String)
        Static iLevel As Int16
        If Mid(Mess, 1, 1) = "+" Then
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "+", "--->", 1, 1))
            iLevel += 1
        ElseIf Mid(Mess, 1, 1) = "-" Then
            iLevel -= 1
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "-", "<---", 1, 1))
        Else
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "=", "    ", 1, 1))
        End If
    End Sub

    Private Sub btnAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddRow.Click
        Me.MyTrace("+frm:btnAddNewCustomer_Click")
        Dim cust As New Customer(Me.Customers)
        cust.FirstName = "First1"
        cust.LastName = "Last1"
        Me.Customers.Add(cust)
        MyTrace("-frm:btnAddNewCustomer_Click")
    End Sub

    Private Sub btnDeleteRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDeleteRow.Click
        Me.MyTrace("+frm:btnDeleteRow_Click")
        Me.Customers.RemoveAt(Me.grdCustomers.CurrentRow.Index)
        Me.grdCustomers.Rows.RemoveAt(Me.grdCustomers.CurrentRow.Index)
        MyTrace("-frm:btnDeleteRow_Click")
    End Sub

End Class



Public Class Customer
    Implements IEditableObject
    Public Event TraceMe(ByVal Mess As String)

    Structure CustomerData
        Friend firstName As String
        Friend lastName As String
    End Structure

    Public parent As CustomersList
    Private custData As CustomerData
    Private backupData As CustomerData
    Private inTxn As Boolean = False

    ' Implements IEditableObject
    Sub BeginEdit() Implements IEditableObject.BeginEdit
        _MyParent.MyTrace1("+Cust:BeginEdit")
        If Not inTxn Then
            Me.backupData = custData
            inTxn = True
        End If
        _MyParent.MyTrace1("-Cust:BeginEdit")
    End Sub

    Sub CancelEdit() Implements IEditableObject.CancelEdit
        _MyParent.MyTrace1("+CancelEdit")
        If inTxn Then
            Me.custData = backupData
            inTxn = False
        End If
        _MyParent.MyTrace1("-CancelEdit")
    End Sub

    Sub EndEdit() Implements IEditableObject.EndEdit
        _MyParent.MyTrace1("+EndEdit")
        If inTxn Then
            backupData = New CustomerData()
            inTxn = False
        End If
        _MyParent.MyTrace1("-EndEdit")
    End Sub

    Private _MyParent As CustomersList
    Public Sub New(ByVal objMyParent As CustomersList)
        _MyParent = objMyParent
        _MyParent.MyTrace1("+Cust:New")
        Me.custData = New CustomerData()
        Me.custData.firstName = ""
        Me.custData.lastName = ""
        _MyParent.MyTrace1("-Cust:New")
    End Sub

    Public Property FirstName() As String
        Get
            _MyParent.MyTrace1("=Cust:FirstName<--" & Me.custData.firstName)
            Return Me.custData.firstName
        End Get
        Set(ByVal Value As String)
            _MyParent.MyTrace1("=Cust:FirstName-->" & Value)
            Me.custData.firstName = Value
            Me.OnCustomerChanged()
        End Set
    End Property

    Public Property LastName() As String
        Get
            _MyParent.MyTrace1("=Cust:LastName<--" & Me.custData.lastName)
            Return Me.custData.lastName
        End Get
        Set(ByVal Value As String)
            _MyParent.MyTrace1("=Cust:LastName-->" & Value)
            Me.custData.lastName = Value
            Me.OnCustomerChanged()
        End Set
    End Property

    Friend Property Parents() As CustomersList
        Get
            _MyParent.MyTrace1("=Cust:Parents")
            Return parent
        End Get
        Set(ByVal Value As CustomersList)
            _MyParent.MyTrace1("=Cust:Parents")
            parent = Value
        End Set
    End Property

    Private Sub OnCustomerChanged()
        If Not inTxn And Not (parent Is Nothing) Then
            parent.CustomerChanged(Me)
        End If
    End Sub

    Public Overrides Function ToString() As String
        Dim sb As New IO.StringWriter()
        sb.Write(Me.FirstName)
        sb.Write(" ")
        sb.Write(Me.LastName)
        Return sb.ToString()
    End Function

End Class



Public Class CustomersList
    Inherits CollectionBase
    Implements IBindingList

    Public Event MyTrace(ByVal Mess As String)

    Private resetEvent As New ListChangedEventArgs(ListChangedType.Reset, -1)
    Private onListChanged1 As ListChangedEventHandler

    Default Public Property Item(ByVal index As Integer) As Customer
        Get
            RaiseEvent MyTrace("=CList:Item<--")
            Return CType(List(index), Customer)
        End Get
        Set(ByVal Value As Customer)
            RaiseEvent MyTrace("=CList:Item-->")
            List(index) = Value
        End Set
    End Property

    Public Function Add(ByVal value As Customer) As Integer
        RaiseEvent MyTrace("=CList:Add")
        Return List.Add(value)
    End Function

    Public Sub Remove(ByVal value As Customer)
        RaiseEvent MyTrace("+>CList:Remove")
        List.Remove(value)
        RaiseEvent MyTrace("-CList:Remove")
    End Sub

    Protected Overridable Sub OnListChanged(ByVal ev As ListChangedEventArgs)
        RaiseEvent MyTrace("+CList:OnListChanged")
        If Not (onListChanged1 Is Nothing) Then
            onListChanged1(Me, ev)
        End If
        RaiseEvent MyTrace("-CList:OnListChanged")
    End Sub

    Protected Overrides Sub OnClear()
        RaiseEvent MyTrace("+CList:OnClear")
        Dim c As Customer
        For Each c In List
            c.parent = Nothing
        Next c
        RaiseEvent MyTrace("-CList:OnClear")
    End Sub

    Protected Overrides Sub OnClearComplete()
        RaiseEvent MyTrace("+CList:OnClearComplete")
        OnListChanged(resetEvent)
        RaiseEvent MyTrace("-CList:OnClearComplete")
    End Sub

    Protected Overrides Sub OnInsertComplete(ByVal index As Integer, ByVal value As Object)
        RaiseEvent MyTrace("+CList:OnInsertComplete")
        Dim c As Customer = CType(value, Customer)
        c.parent = Me
        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
        RaiseEvent MyTrace("-CList:OnInsertComplete")
    End Sub

    Protected Overrides Sub OnRemoveComplete(ByVal index As Integer, ByVal value As Object)
        RaiseEvent MyTrace("+CList:OnRemoveComplete")
        Dim c As Customer = CType(value, Customer)
        c.parent = Me
        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
        RaiseEvent MyTrace("-CList:OnRemoveComplete")
    End Sub

    Protected Overrides Sub OnSetComplete(ByVal index As Integer, ByVal oldValue As Object, ByVal newValue As Object)
        RaiseEvent MyTrace("+CList:OnSetComplete")
        If oldValue <> newValue Then
            Dim oldcust As Customer = CType(oldValue, Customer)
            Dim newcust As Customer = CType(newValue, Customer)
            oldcust.parent = Nothing
            newcust.parent = Me
            OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
        End If
        RaiseEvent MyTrace("-CList:OnSetComplete")
    End Sub

    Friend Sub CustomerChanged(ByVal cust As Customer)
        RaiseEvent MyTrace("+CList:CustomerChanged")
        Dim index As Integer = List.IndexOf(cust)
        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemChanged, index))
        RaiseEvent MyTrace("-CList:CustomerChanged")
    End Sub

    ' Implements IBindingList.
    ReadOnly Property AllowEdit() As Boolean Implements IBindingList.AllowEdit
        Get
            Return True
        End Get
    End Property

    ReadOnly Property AllowNew() As Boolean Implements IBindingList.AllowNew
        Get
            Return True
        End Get
    End Property

    ReadOnly Property AllowRemove() As Boolean Implements IBindingList.AllowRemove
        Get
            Return True
        End Get
    End Property

    ReadOnly Property SupportsChangeNotification() As Boolean Implements IBindingList.SupportsChangeNotification
        Get
            Return True
        End Get
    End Property

    ReadOnly Property SupportsSearching() As Boolean Implements IBindingList.SupportsSearching
        Get
            Return False
        End Get
    End Property

    ReadOnly Property SupportsSorting() As Boolean Implements IBindingList.SupportsSorting
        Get
            Return False
        End Get
    End Property

    ' Events.
    Public Event ListChanged As ListChangedEventHandler Implements IBindingList.ListChanged

    ' Methods.
    Function AddNew() As Object Implements IBindingList.AddNew
        RaiseEvent MyTrace("+CList:AddNew")
        Dim c As New Customer(Me)
        List.Add(c)
        RaiseEvent MyTrace("-CList:AddNew")
        Return c
    End Function

    ' Unsupported properties.
    ReadOnly Property IsSorted() As Boolean Implements IBindingList.IsSorted
        Get
            Throw New NotSupportedException()
        End Get
    End Property

    ReadOnly Property SortDirection() As ListSortDirection Implements IBindingList.SortDirection
        Get
            Throw New NotSupportedException()
        End Get
    End Property

    ReadOnly Property SortProperty() As PropertyDescriptor Implements IBindingList.SortProperty
        Get
            Throw New NotSupportedException()
        End Get
    End Property

    ' Unsupported Methods.
    Sub AddIndex(ByVal prop As PropertyDescriptor) Implements IBindingList.AddIndex
        Throw New NotSupportedException()
    End Sub

    Sub ApplySort(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection) Implements IBindingList.ApplySort
        Throw New NotSupportedException()
    End Sub

    Function Find(ByVal prop As PropertyDescriptor, ByVal key As Object) As Integer Implements IBindingList.Find
        Throw New NotSupportedException()
    End Function

    Sub RemoveIndex(ByVal prop As PropertyDescriptor) Implements IBindingList.RemoveIndex
        Throw New NotSupportedException()
    End Sub

    Sub RemoveSort() Implements IBindingList.RemoveSort
        Throw New NotSupportedException()
    End Sub

    Friend Sub MyTrace1(ByVal Mess As String)
        RaiseEvent MyTrace(Mess)
    End Sub

End Class
'------------------------------------------------------------------------------------------------
0
Comment
Question by:gem56
  • 5
  • 3
8 Comments
 
LVL 13

Expert Comment

by:Corey Scheich
ID: 19610694
instead of using CollectionBase use

Inherits BindingList(Of Customer)

It requires allot less code and already implements everything needed for binding
0
 
LVL 13

Expert Comment

by:Corey Scheich
ID: 19611166
I modified your code and tested it works well.

' ------------------------------------------------------------------------------------------------
Imports System.ComponentModel           ' Needed for using <Bindable(True)>

Public Class frmCustomers





    Dim Customers As New CustomersList

    Private Sub frmCustomers_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        MyTrace("+frmCust:LoadCustomers")
        Try

            AddHandler Customers.MyTrace, AddressOf MyTrace
            Dim l As IList = CType(Customers, IList)
            l.Add(ReadCustomer(1))
            l.Add(ReadCustomer(2))
            l.Add(ReadCustomer(3))
            l.Add(ReadCustomer(4))
            Me.grdCustomers.DataSource = Customers
            Me.grdCustomers.Refresh()
            MyTrace("-frmCust:LoadCustomers")
        Catch ex As Exception
            MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
        End Try
    End Sub

    Private Function ReadCustomer(ByVal n As Int16) As Customer
        MyTrace("+CList:ReadCustomer")
        Dim cust As New Customer(Customers)
        cust.FirstName = "First" & n
        cust.LastName = "Last" & n
        MyTrace("-CList:ReadCustomer`")
        Return cust
    End Function

    Private Sub MyTrace(ByVal Mess As String)
        Static iLevel As Int16
        If Mid(Mess, 1, 1) = "+" Then
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "+", "--->", 1, 1))
            iLevel += 1
        ElseIf Mid(Mess, 1, 1) = "-" Then
            iLevel -= 1
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "-", "<---", 1, 1))
        Else
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "=", "    ", 1, 1))
        End If
    End Sub

    Private Sub btnAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddRow.Click
        Me.MyTrace("+frm:btnAddNewCustomer_Click")
        Dim cust As New Customer(Me.Customers)
        cust.FirstName = "First1"
        cust.LastName = "Last1"
        Me.Customers.Add(cust)
        MyTrace("-frm:btnAddNewCustomer_Click")
    End Sub

    Private Sub btnDeleteRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDeleteRow.Click
        Me.MyTrace("+frm:btnDeleteRow_Click")
        Me.Customers.RemoveAt(Me.grdCustomers.CurrentRow.Index)
        Me.grdCustomers.Rows.RemoveAt(Me.grdCustomers.CurrentRow.Index)
        MyTrace("-frm:btnDeleteRow_Click")
    End Sub

End Class



Public Class Customer
    Implements IEditableObject
    Public Event TraceMe(ByVal Mess As String)

    Structure CustomerData
        Friend firstName As String
        Friend lastName As String
    End Structure

    Public parent As CustomersList
    Private custData As CustomerData
    Private backupData As CustomerData
    Private inTxn As Boolean = False

    ' Implements IEditableObject
    Sub BeginEdit() Implements IEditableObject.BeginEdit
        _MyParent.MyTrace1("+Cust:BeginEdit")
        If Not inTxn Then
            Me.backupData = custData
            inTxn = True
        End If
        _MyParent.MyTrace1("-Cust:BeginEdit")
    End Sub

    Sub CancelEdit() Implements IEditableObject.CancelEdit
        _MyParent.MyTrace1("+CancelEdit")
        If inTxn Then
            Me.custData = backupData
            inTxn = False
        End If
        _MyParent.MyTrace1("-CancelEdit")
    End Sub

    Sub EndEdit() Implements IEditableObject.EndEdit
        _MyParent.MyTrace1("+EndEdit")
        If inTxn Then
            backupData = New CustomerData()
            inTxn = False
        End If
        _MyParent.MyTrace1("-EndEdit")
    End Sub

    Private _MyParent As CustomersList
    Public Sub New(ByVal objMyParent As CustomersList)
        _MyParent = objMyParent
        _MyParent.MyTrace1("+Cust:New")
        Me.custData = New CustomerData()
        Me.custData.firstName = ""
        Me.custData.lastName = ""
        _MyParent.MyTrace1("-Cust:New")
    End Sub

    Public Property FirstName() As String
        Get
            _MyParent.MyTrace1("=Cust:FirstName<--" & Me.custData.firstName)
            Return Me.custData.firstName
        End Get
        Set(ByVal Value As String)
            _MyParent.MyTrace1("=Cust:FirstName-->" & Value)
            Me.custData.firstName = Value
            Me.OnCustomerChanged()
        End Set
    End Property

    Public Property LastName() As String
        Get
            _MyParent.MyTrace1("=Cust:LastName<--" & Me.custData.lastName)
            Return Me.custData.lastName
        End Get
        Set(ByVal Value As String)
            _MyParent.MyTrace1("=Cust:LastName-->" & Value)
            Me.custData.lastName = Value
            Me.OnCustomerChanged()
        End Set
    End Property

    Friend Property Parents() As CustomersList
        Get
            _MyParent.MyTrace1("=Cust:Parents")
            Return parent
        End Get
        Set(ByVal Value As CustomersList)
            _MyParent.MyTrace1("=Cust:Parents")
            parent = Value
        End Set
    End Property

    Private Sub OnCustomerChanged()
        If Not inTxn And Not (parent Is Nothing) Then
            parent.CustomerChanged(Me)
        End If
    End Sub

    Public Overrides Function ToString() As String
        Dim sb As New IO.StringWriter()
        sb.Write(Me.FirstName)
        sb.Write(" ")
        sb.Write(Me.LastName)
        Return sb.ToString()
    End Function

End Class



Public Class CustomersList
    Inherits BindingList(Of Customer)
    '    Implements INotifyPropertyChanged


    Public Event MyTrace(ByVal Mess As String)

    '    Private resetEvent As New ListChangedEventArgs(ListChangedType.Reset, -1)
    '    Private onListChanged1 As ListChangedEventHandler

    '    Default Public Property Item(ByVal index As Integer) As Customer
    '        Get
    '            RaiseEvent MyTrace("=CList:Item<--")
    '            Return CType(List(index), Customer)
    '        End Get
    '        Set(ByVal Value As Customer)
    '            RaiseEvent MyTrace("=CList:Item-->")
    '            List(index) = Value
    '        End Set
    '    End Property

    '    Public Function Add(ByVal value As Customer) As Integer
    '        RaiseEvent MyTrace("=CList:Add")
    '        Return MyBase.List.Add(value)
    '    End Function

    '    Public Sub Remove(ByVal value As Customer)
    '        RaiseEvent MyTrace("+>CList:Remove")
    '        List.Remove(value)
    '        RaiseEvent MyTrace("-CList:Remove")
    '    End Sub

    '    Protected Overridable Sub OnListChanged(ByVal ev As ListChangedEventArgs)
    '        RaiseEvent MyTrace("+CList:OnListChanged")
    '        If Not (onListChanged1 Is Nothing) Then
    '            onListChanged1(Me, ev)
    '        End If
    '        RaiseEvent MyTrace("-CList:OnListChanged")
    '    End Sub

    '    Protected Overrides Sub OnClear()
    '        RaiseEvent MyTrace("+CList:OnClear")
    '        Dim c As Customer
    '        For Each c In List
    '            c.parent = Nothing
    '        Next c
    '        RaiseEvent MyTrace("-CList:OnClear")
    '    End Sub

    '    Protected Overrides Sub OnClearComplete()
    '        RaiseEvent MyTrace("+CList:OnClearComplete")
    '        OnListChanged(resetEvent)
    '        RaiseEvent MyTrace("-CList:OnClearComplete")
    '    End Sub

    '    Protected Overrides Sub OnInsertComplete(ByVal index As Integer, ByVal value As Object)
    '        RaiseEvent MyTrace("+CList:OnInsertComplete")
    '        Dim c As Customer = CType(value, Customer)
    '        c.parent = Me
    '        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    '        RaiseEvent MyTrace("-CList:OnInsertComplete")
    '    End Sub

    '    Protected Overrides Sub OnRemoveComplete(ByVal index As Integer, ByVal value As Object)
    '        RaiseEvent MyTrace("+CList:OnRemoveComplete")
    '        Dim c As Customer = CType(value, Customer)
    '        c.parent = Me
    '        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
    '        RaiseEvent MyTrace("-CList:OnRemoveComplete")
    '    End Sub

    '    Protected Overrides Sub OnSetComplete(ByVal index As Integer, ByVal oldValue As Object, ByVal newValue As Object)
    '        RaiseEvent MyTrace("+CList:OnSetComplete")
    '        If oldValue <> newValue Then
    '            Dim oldcust As Customer = CType(oldValue, Customer)
    '            Dim newcust As Customer = CType(newValue, Customer)
    '            oldcust.parent = Nothing
    '            newcust.parent = Me
    '            OnListChanged(New ListChangedEventArgs(ListChangedType.ItemAdded, index))
    '        End If
    '        RaiseEvent MyTrace("-CList:OnSetComplete")
    '    End Sub

    Friend Sub CustomerChanged(ByVal cust As Customer)
        RaiseEvent MyTrace("+CList:CustomerChanged")
        Dim index As Integer = Me.IndexOf(cust)
        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemChanged, index))
        RaiseEvent MyTrace("-CList:CustomerChanged")
    End Sub

    '    ' Implements IBindingList.
    '    ReadOnly Property AllowEdit() As Boolean Implements IBindingList.AllowEdit
    '        Get
    '            Return True
    '        End Get
    '    End Property

    '    ReadOnly Property AllowNew() As Boolean Implements IBindingList.AllowNew
    '        Get
    '            Return True
    '        End Get
    '    End Property

    '    ReadOnly Property AllowRemove() As Boolean Implements IBindingList.AllowRemove
    '        Get
    '            Return True
    '        End Get
    '    End Property

    '    ReadOnly Property SupportsChangeNotification() As Boolean Implements IBindingList.SupportsChangeNotification
    '        Get
    '            Return True
    '        End Get
    '    End Property

    '    ReadOnly Property SupportsSearching() As Boolean Implements IBindingList.SupportsSearching
    '        Get
    '            Return False
    '        End Get
    '    End Property

    '    ReadOnly Property SupportsSorting() As Boolean Implements IBindingList.SupportsSorting
    '        Get
    '            Return False
    '        End Get
    '    End Property

    '    ' Events.
    '    Public Event ListChanged As ListChangedEventHandler Implements IBindingList.ListChanged

    '    ' Methods.
    '    Function AddNew() As Object Implements IBindingList.AddNew
    '        RaiseEvent MyTrace("+CList:AddNew")
    '        Dim c As New Customer(Me)
    '        List.Add(c)
    '        RaiseEvent MyTrace("-CList:AddNew")
    '        Return c
    '    End Function

    '    ' Unsupported properties.
    '    ReadOnly Property IsSorted() As Boolean Implements IBindingList.IsSorted
    '        Get
    '            Throw New NotSupportedException()
    '        End Get
    '    End Property

    '    ReadOnly Property SortDirection() As ListSortDirection Implements IBindingList.SortDirection
    '        Get
    '            Throw New NotSupportedException()
    '        End Get
    '    End Property

    '    ReadOnly Property SortProperty() As PropertyDescriptor Implements IBindingList.SortProperty
    '        Get
    '            Throw New NotSupportedException()
    '        End Get
    '    End Property

    '    ' Unsupported Methods.
    '    Sub AddIndex(ByVal prop As PropertyDescriptor) Implements IBindingList.AddIndex
    '        Throw New NotSupportedException()
    '    End Sub

    '    Sub ApplySort(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection) Implements IBindingList.ApplySort
    '        Throw New NotSupportedException()
    '    End Sub

    '    Function Find(ByVal prop As PropertyDescriptor, ByVal key As Object) As Integer Implements IBindingList.Find
    '        Throw New NotSupportedException()
    '    End Function

    '    Sub RemoveIndex(ByVal prop As PropertyDescriptor) Implements IBindingList.RemoveIndex
    '        Throw New NotSupportedException()
    '    End Sub

    '    Sub RemoveSort() Implements IBindingList.RemoveSort
    '        Throw New NotSupportedException()
    '    End Sub

    Friend Sub MyTrace1(ByVal Mess As String)
        RaiseEvent MyTrace(Mess)
    End Sub

    '    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
0
 

Author Comment

by:gem56
ID: 19613968
Hi Corey2,
Thanks a lot, that's exactly what I need. Seeing that you know your stuff ;-) maybe you can help me with a few additional questions.

1) If I programmatically change the underlying data it doesn't get reflected in DataGridView unles I execute 'grdCustomers.UpdateCellValue'. It's no big deal but I was just wondering if that is just the way it is or can that be automated?
2) I can now insert rows programmatically to the collection but not directly into DataGridView. I don't need to do that now but, for future reference, is that just the way it is?
3) What' the easies way of detecting change in Cell contents? I was going to use 'IEditableObject.EndEdit' but is there a better way?

Cheers,
     Michael
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 13

Expert Comment

by:Corey Scheich
ID: 19618409
1) Modify your
Public Class Customer
          Implements INotifyPropertyChanged

'then you must raise the event through the set method of the property
'this will notify the list that a change has been made to one of the items

    Public Property FirstName() As String
        Get
            _MyParent.MyTrace1("=Cust:FirstName<--" & Me.custData.firstName)
            Return Me.custData.firstName
        End Get
        Set(ByVal Value As String)
            _MyParent.MyTrace1("=Cust:FirstName-->" & Value)
            Me.custData.firstName = Value
            Me.OnCustomerChanged()
'the following 2 lines need to be added to every property to raise the event
'no edits need to be made to either line they will send the appropriate data no matter what
            Dim PropName As String = System.Reflection.MethodBase.GetCurrentMethod.Name
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(PropName.Remove(0, 4)))
        End Set
    End Property

2) You cannot add items because there is not Sub New for the Customer Class that doesn't require a parameter

3)     Private Sub grdCustomers_CellEndEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdCustomers.CellEndEdit

    End Sub
0
 

Author Comment

by:gem56
ID: 19623207
Once again, you were spot on so thanks a lot.

Just one final clarification. For point 2) you said that I'm unable to (programmatically) insert rows to DataGridView because my Customer Class doesn't have Sub New. I created an empty Sub New (not sure whether it needs anything though) and I'm getting the same response "Rows cannot be programmatically added to the dataGridView's rows collection when the control data-bound".

Maybe I've misunderstood you or should the Sub New have something in it?

I'm still interested on this point because if that was possible than (for easier maintenance) I could off load some functionality into the DataGridView rather than have it in my other class.

Cheers,
     Michael
0
 
LVL 13

Expert Comment

by:Corey Scheich
ID: 19625256
You currently have the following code in the customer class
 
   Private _MyParent As CustomersList
    Public Sub New(ByVal objMyParent As CustomersList)
        _MyParent = objMyParent
        _MyParent.MyTrace1("+Cust:New")
        Me.custData = New CustomerData()
        Me.custData.firstName = ""
        Me.custData.lastName = ""
        _MyParent.MyTrace1("-Cust:New")
    End Sub

'add this code
Public Sub New() 'donot add any params
'add any code you need here
end sub

That is all I did to get the Datagrid to work with the example I posted above.
0
 
LVL 13

Accepted Solution

by:
Corey Scheich earned 2000 total points
ID: 19627020
Here is the code that works on my end. values can be programatically updated items can be programatically added or simply added using the form. Events can be cought when entering and leaving a cell edit.

' ------------------------------------------------------------------------------------------------
Imports System.ComponentModel           ' Needed for using <Bindable(True)>

Public Class frmCustomers
    Dim Customers As New CustomersList

    Private Sub frmCustomers_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        MyTrace("+frmCust:LoadCustomers")
        Try

            AddHandler Customers.MyTrace, AddressOf MyTrace
            Dim l As CustomersList = Customers
            l.Add(ReadCustomer(1))
            l.Add(ReadCustomer(2))
            l.Add(ReadCustomer(3))
            l.Add(ReadCustomer(4))
            Me.grdCustomers.DataSource = Customers
            Me.grdCustomers.Refresh()
            MyTrace("-frmCust:LoadCustomers")
        Catch ex As Exception
            MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
        End Try
    End Sub

    Private Function ReadCustomer(ByVal n As Int16) As Customer
        MyTrace("+CList:ReadCustomer")
        Dim cust As New Customer(Customers)
        cust.FirstName = "First" & n
        cust.LastName = "Last" & n
        MyTrace("-CList:ReadCustomer`")
        Return cust
    End Function

    Private Sub MyTrace(ByVal Mess As String)
        Static iLevel As Int16
        If Mid(Mess, 1, 1) = "+" Then
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "+", "--->", 1, 1))
            iLevel += 1
        ElseIf Mid(Mess, 1, 1) = "-" Then
            iLevel -= 1
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "-", "<---", 1, 1))
        Else
            Debug.Print(Space(iLevel * 4) & Replace(Mess, "=", "    ", 1, 1))
        End If
    End Sub

    Private Sub btnAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddRow.Click
        Me.MyTrace("+frm:btnAddNewCustomer_Click")
        Dim cust As New Customer(Me.Customers)
        cust.FirstName = "First1"
        cust.LastName = "Last1"
        Me.Customers.Add(cust)
        MyTrace("-frm:btnAddNewCustomer_Click")
    End Sub

    Private Sub btnDeleteRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDeleteRow.Click
        Me.MyTrace("+frm:btnDeleteRow_Click")
        Me.grdCustomers.Rows.RemoveAt(Me.grdCustomers.CurrentRow.Index)
        MyTrace("-frm:btnDeleteRow_Click")
    End Sub

    Private Sub grdCustomers_CancelRowEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.QuestionEventArgs) Handles grdCustomers.CancelRowEdit

    End Sub

    Private Sub grdCustomers_CellBeginEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles grdCustomers.CellBeginEdit

    End Sub

    Private Sub grdCustomers_CellEndEdit(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdCustomers.CellEndEdit

    End Sub

    Private Sub grdCustomers_CellEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdCustomers.CellEnter

    End Sub

    Private Sub grdCustomers_CellLeave(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles grdCustomers.CellLeave

    End Sub
End Class

Public Class Customer
    Implements IEditableObject
    Implements INotifyPropertyChanged

    Public Event TraceMe(ByVal Mess As String)

    Structure CustomerData
        Friend firstName As String
        Friend lastName As String
    End Structure

    Public parent As CustomersList
    Private custData As CustomerData
    Private backupData As CustomerData
    Private inTxn As Boolean = False

    Sub BeginEdit() Implements IEditableObject.BeginEdit
        If Not inTxn Then
            Me.backupData = custData
            inTxn = True
        End If
    End Sub

    Sub CancelEdit() Implements IEditableObject.CancelEdit
        If inTxn Then
            Me.custData = backupData
            inTxn = False
        End If
    End Sub

    Sub EndEdit() Implements IEditableObject.EndEdit
        If inTxn Then
            backupData = New CustomerData()
            inTxn = False
        End If
    End Sub

    Private _MyParent As CustomersList
    Public Sub New(ByVal objMyParent As CustomersList)
        _MyParent = objMyParent
        _MyParent.MyTrace1("+Cust:New")
        Me.custData = New CustomerData()
        Me.custData.firstName = ""
        Me.custData.lastName = ""
        _MyParent.MyTrace1("-Cust:New")
    End Sub

    Public Sub New()

    End Sub

    Public Property FirstName() As String
        Get
            Return Me.custData.firstName
        End Get
        Set(ByVal Value As String)
            Me.custData.firstName = Value
            Me.OnCustomerChanged()
            Dim PropName As String = System.Reflection.MethodBase.GetCurrentMethod.Name
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(PropName.Remove(0, 4)))
        End Set
    End Property

    Public Property LastName() As String
        Get
            Return Me.custData.lastName
        End Get
        Set(ByVal Value As String)
            Me.custData.lastName = Value
            Me.OnCustomerChanged()
            Dim PropName As String = System.Reflection.MethodBase.GetCurrentMethod.Name
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(PropName.Remove(0, 4)))
        End Set
    End Property

    Friend Property Parents() As CustomersList
        Get
            Return parent
        End Get
        Set(ByVal Value As CustomersList)
            parent = Value
            Dim PropName As String = System.Reflection.MethodBase.GetCurrentMethod.Name
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(PropName.Remove(0, 4)))
        End Set
    End Property

    Private Sub OnCustomerChanged()
        If Not inTxn And Not (parent Is Nothing) Then
            parent.CustomerChanged(Me)
        End If
    End Sub

    Public Overrides Function ToString() As String
        Dim sb As New IO.StringWriter()
        sb.Write(Me.FirstName)
        sb.Write(" ")
        sb.Write(Me.LastName)
        Return sb.ToString()
    End Function

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class

Public Class CustomersList
    Inherits BindingList(Of Customer)

    Public Event MyTrace(ByVal Mess As String)

    Friend Sub CustomerChanged(ByVal cust As Customer)
        RaiseEvent MyTrace("+CList:CustomerChanged")
        Dim index As Integer = Me.IndexOf(cust)
        OnListChanged(New ListChangedEventArgs(ListChangedType.ItemChanged, index))
        RaiseEvent MyTrace("-CList:CustomerChanged")
    End Sub
    Friend Sub MyTrace1(ByVal Mess As String)
        RaiseEvent MyTrace(Mess)
    End Sub
End Class
0
 

Author Comment

by:gem56
ID: 19629993
Hi Corey2,
On my end everything else works except for the insertion of a row into DGV (using code) however as you've answered the initial question (plus some) I've decided to close this question and open another "http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_22741618.html", and linked it to this question.
If you don't mind I'd appreciate you commnting on the new question, as you know exactly what's qoing on and have been most helpful.

Thanks a lot,
     Michael
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I think the Typed DataTable and Typed DataSet are very good options when working with data, but I don't like auto-generated code. First, I create an Abstract Class for my DataTables Common Code.  This class Inherits from DataTable. Also, it can …
1.0 - Introduction Converting Visual Basic 6.0 (VB6) to Visual Basic 2008+ (VB.NET). If ever there was a subject full of murkiness and bad decisions, it is this one!   The first problem seems to be that people considering this task of converting…
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Whether it be Exchange Server Crash Issues, Dirty Shutdown Errors or Failed to mount error, Stellar Phoenix Mailbox Exchange Recovery has always got your back. With the help of its easy to understand user interface and 3 simple steps recovery proced…
Suggested Courses

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question