gem56
asked on
Problem implementing data binding with DataGridView
Hi guys,
This question is related to my previous question "https://www.experts-exchange.com/questions/22734276/Problem-implementing-data-binding-with-DataGridView.html". As the initial question was answered, and me having additional issues, I decided to open (this) new question.
The code below shows an example that I'm using to data-bind a DataGridView control with an object collection. To run this code just create a Form with a DataGridView control and two buttons (called grdCustomers, btnObjectAddRow and btnDGVAddRow), that I use to: 1) add a new object to the collection and 2) add a row to the DatGridView control.
I am able to (programmatically) delete & modify data/rows via either DataGridView and the Collection bject however I can only add rows via the underlying data and not the DataGridView. When I try to add a row into DataGridView (using button btnDGVAddRow) I get an error message "Rows cannot be programmatically added to the DataGridView's rows collection when the control data-bound".
/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
Try
LoadCustomers()
Me.grdCustomers.DataSource = Customers
Me.grdCustomers.Refresh()
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End Sub
Private Sub LoadCustomers()
Try
Dim l As IList = CType(Customers, IList)
For n As Int16 = 1 To 5
l.Add(ReadCustomer(l.Count ))
Next
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, Optional ByVal bEmpty As Boolean = False) As Customer
Dim cust As New Customer(Customers)
If bEmpty = False Then
cust.FirstName = "First" & n
cust.LastName = "Last" & n
End If
Return cust
End Function
Private Sub grdCustomers_CancelRowEdit (ByVal sender As Object, ByVal e As System.Windows.Forms.Quest ionEventAr gs)
End Sub
Private Sub grdCustomers_CellBeginEdit (ByVal sender As Object, ByVal e As System.Windows.Forms.DataG ridViewCel lCancelEve ntArgs)
End Sub
Private Sub grdCustomers_CellEndEdit(B yVal sender As Object, ByVal e As System.Windows.Forms.DataG ridViewCel lEventArgs )
End Sub
Private Sub grdCustomers_CellEnter(ByV al sender As Object, ByVal e As System.Windows.Forms.DataG ridViewCel lEventArgs )
End Sub
Private Sub grdCustomers_CellLeave(ByV al sender As Object, ByVal e As System.Windows.Forms.DataG ridViewCel lEventArgs )
End Sub
Private Sub btnObjectAddRow_Click(ByVa l sender As System.Object, ByVal e As System.EventArgs) Handles btnObjectAddRow.Click
Dim cust As New Customer(Me.Customers)
cust.FirstName = "First" & Me.grdCustomers.Rows.Count + 1
cust.LastName = "Last" & Me.grdCustomers.Rows.Count + 1
Me.Customers.Insert(Me.grd Customers. CurrentRow .Index, cust)
End Sub
Private Sub btnObjectDeleteRow_Click(B yVal sender As System.Object, ByVal e As System.EventArgs) Handles btnObjectDeleteRow.Click
Me.Customers.RemoveAt(Me.g rdCustomer s.CurrentR ow.Index)
End Sub
Private Sub btnObjectModifyRow_Click(B yVal sender As System.Object, ByVal e As System.EventArgs) Handles btnObjectModifyRow.Click
With Me.Customers.Item(Me.grdCu stomers.Cu rrentRow.I ndex)
If Me.grdCustomers.CurrentCel l.ColumnIn dex = 0 Then
.LastName = .LastName & Format(Now, "ss")
Else
.FirstName = .FirstName & Format(Now, "ss")
End If
End With
End Sub
Private Sub btnDGVAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVAddRow.Click
With Me.grdCustomers.CurrentCel l
Try
Me.grdCustomers.Rows.Inser t(Me.grdCu stomers.Cu rrentCell. RowIndex)
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End With
End Sub
Private Sub btnDGVDeleteRow_Click(ByVa l sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVDeleteRow.Click
Me.grdCustomers.Rows.Remov eAt(Me.grd Customers. CurrentRow .Index)
End Sub
Private Sub btnDGVModifyRow_Click(ByVa l sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVModifyRow.Click
With Me.grdCustomers.CurrentCel l
.Value = .Value & Format(Now, "ss")
End With
End Sub
Private Sub btnObjectReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnObjectReset.Click
Customers.Clear()
LoadCustomers()
End Sub
Private Sub btnDGVReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVReset.Click
Me.grdCustomers.Rows.Clear ()
LoadCustomers()
End Sub
End Class
Public Class CustomersList
Inherits BindingList(Of Customer)
Friend Sub CustomerChanged(ByVal cust As Customer)
Dim index As Integer = Me.IndexOf(cust)
OnListChanged(New ListChangedEventArgs(ListC hangedType .ItemChang ed, index))
End Sub
End Class
Public Class Customer
Implements IEditableObject
Implements INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.Prop ertyChange dEventArgs ) Implements System.ComponentModel.INot ifyPropert yChanged.P ropertyCha nged
Structure CustomerData
Friend firstName As String
Friend lastName As String
End Structure
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
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
Public Sub New(ByVal objMyParent As CustomersList)
_MyParent = objMyParent
custData = New CustomerData()
custData.firstName = ""
custData.lastName = ""
End Sub
Public Sub New()
End Sub
Public Property FirstName() As String
Get
Return custData.firstName
End Get
Set(ByVal Value As String)
custData.firstName = Value
Me.OnCustomerChanged()
Dim PropName As String = System.Reflection.MethodBa se.GetCurr entMethod. Name
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop ertyChange dEventArgs (PropName. Remove(0, 4)))
End Set
End Property
Public Property LastName() As String
Get
Return custData.lastName
End Get
Set(ByVal Value As String)
custData.lastName = Value
Me.OnCustomerChanged()
Dim PropName As String = System.Reflection.MethodBa se.GetCurr entMethod. Name
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop ertyChange dEventArgs (PropName. Remove(0, 4)))
End Set
End Property
Private _MyParent As CustomersList
Friend Property MyParent() As CustomersList
Get
Return _MyParent
End Get
Set(ByVal Value As CustomersList)
_MyParent = Value
Dim PropName As String = System.Reflection.MethodBa se.GetCurr entMethod. Name
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop ertyChange dEventArgs (PropName. Remove(0, 4)))
End Set
End Property
Private Sub OnCustomerChanged()
If Not inTxn And Not (_MyParent Is Nothing) Then
_MyParent.CustomerChanged( Me)
End If
End Sub
End Class
This question is related to my previous question "https://www.experts-exchange.com/questions/22734276/Problem-implementing-data-binding-with-DataGridView.html". As the initial question was answered, and me having additional issues, I decided to open (this) new question.
The code below shows an example that I'm using to data-bind a DataGridView control with an object collection. To run this code just create a Form with a DataGridView control and two buttons (called grdCustomers, btnObjectAddRow and btnDGVAddRow), that I use to: 1) add a new object to the collection and 2) add a row to the DatGridView control.
I am able to (programmatically) delete & modify data/rows via either DataGridView and the Collection bject however I can only add rows via the underlying data and not the DataGridView. When I try to add a row into DataGridView (using button btnDGVAddRow) I get an error message "Rows cannot be programmatically added to the DataGridView's rows collection when the control data-bound".
/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
Try
LoadCustomers()
Me.grdCustomers.DataSource
Me.grdCustomers.Refresh()
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End Sub
Private Sub LoadCustomers()
Try
Dim l As IList = CType(Customers, IList)
For n As Int16 = 1 To 5
l.Add(ReadCustomer(l.Count
Next
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, Optional ByVal bEmpty As Boolean = False) As Customer
Dim cust As New Customer(Customers)
If bEmpty = False Then
cust.FirstName = "First" & n
cust.LastName = "Last" & n
End If
Return cust
End Function
Private Sub grdCustomers_CancelRowEdit
End Sub
Private Sub grdCustomers_CellBeginEdit
End Sub
Private Sub grdCustomers_CellEndEdit(B
End Sub
Private Sub grdCustomers_CellEnter(ByV
End Sub
Private Sub grdCustomers_CellLeave(ByV
End Sub
Private Sub btnObjectAddRow_Click(ByVa
Dim cust As New Customer(Me.Customers)
cust.FirstName = "First" & Me.grdCustomers.Rows.Count
cust.LastName = "Last" & Me.grdCustomers.Rows.Count
Me.Customers.Insert(Me.grd
End Sub
Private Sub btnObjectDeleteRow_Click(B
Me.Customers.RemoveAt(Me.g
End Sub
Private Sub btnObjectModifyRow_Click(B
With Me.Customers.Item(Me.grdCu
If Me.grdCustomers.CurrentCel
.LastName = .LastName & Format(Now, "ss")
Else
.FirstName = .FirstName & Format(Now, "ss")
End If
End With
End Sub
Private Sub btnDGVAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVAddRow.Click
With Me.grdCustomers.CurrentCel
Try
Me.grdCustomers.Rows.Inser
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End With
End Sub
Private Sub btnDGVDeleteRow_Click(ByVa
Me.grdCustomers.Rows.Remov
End Sub
Private Sub btnDGVModifyRow_Click(ByVa
With Me.grdCustomers.CurrentCel
.Value = .Value & Format(Now, "ss")
End With
End Sub
Private Sub btnObjectReset_Click(ByVal
Customers.Clear()
LoadCustomers()
End Sub
Private Sub btnDGVReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVReset.Click
Me.grdCustomers.Rows.Clear
LoadCustomers()
End Sub
End Class
Public Class CustomersList
Inherits BindingList(Of Customer)
Friend Sub CustomerChanged(ByVal cust As Customer)
Dim index As Integer = Me.IndexOf(cust)
OnListChanged(New ListChangedEventArgs(ListC
End Sub
End Class
Public Class Customer
Implements IEditableObject
Implements INotifyPropertyChanged
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.Prop
Structure CustomerData
Friend firstName As String
Friend lastName As String
End Structure
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
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
Public Sub New(ByVal objMyParent As CustomersList)
_MyParent = objMyParent
custData = New CustomerData()
custData.firstName = ""
custData.lastName = ""
End Sub
Public Sub New()
End Sub
Public Property FirstName() As String
Get
Return custData.firstName
End Get
Set(ByVal Value As String)
custData.firstName = Value
Me.OnCustomerChanged()
Dim PropName As String = System.Reflection.MethodBa
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop
End Set
End Property
Public Property LastName() As String
Get
Return custData.lastName
End Get
Set(ByVal Value As String)
custData.lastName = Value
Me.OnCustomerChanged()
Dim PropName As String = System.Reflection.MethodBa
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop
End Set
End Property
Private _MyParent As CustomersList
Friend Property MyParent() As CustomersList
Get
Return _MyParent
End Get
Set(ByVal Value As CustomersList)
_MyParent = Value
Dim PropName As String = System.Reflection.MethodBa
RaiseEvent PropertyChanged(Me, New System.ComponentModel.Prop
End Set
End Property
Private Sub OnCustomerChanged()
If Not inTxn And Not (_MyParent Is Nothing) Then
_MyParent.CustomerChanged(
End If
End Sub
End Class
Can I ask WHY you want to do this? First, I think if you use the "New" line in the datagridview - the last one, marked with "*" - it will work (tho' by user entry, rather than programmatically). Secondly, if you want to do it programmatically, why not simply add it to the collection and let the binding then display it in the datagridview? So, for instance, this works for me
Private Sub btnDGVAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVAddRow.Click
With Me.grdCustomers.CurrentCel l
Try
Dim cust As New Customer(Me.Customers)
cust.FirstName = "" '<< you put what you want here ...
cust.LastName = "" '... and here
Me.Customers.Insert(Me.grd Customers. CurrentRow .Index, cust)
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End With
End Sub
I'm not saying that it might not be possible. I haven't thought it through. But the end result seems to me to be readily achievable without such thought. And, in my view, doing it in the collection rather than trying to do it via the DataGridView is more consistent with the model with which we're dealing. The DataGridView is really just a Display (and Editing) mechanism for the data which is actually in the collection. If the _user_ has to see, add to, alter, delete such data then it is the DataGridView that provides the necessary user-interface. But if the _program_ needs to do any of those things it has direct access to the collection. Once the program has done any of those things, they do need to be reflected in what the user sees - but the binding copes with that. So, using the program to alter the _datagridview_ is just introducing an unnecessary step in the process. Rather than the program telling the collection and the binding passing that on to the datagridview, the program has to tell the datagridview then the binding has to tell the collection then the binding has to tell the datagridview.
So, as I say, why?
Roger
Private Sub btnDGVAddRow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDGVAddRow.Click
With Me.grdCustomers.CurrentCel
Try
Dim cust As New Customer(Me.Customers)
cust.FirstName = "" '<< you put what you want here ...
cust.LastName = "" '... and here
Me.Customers.Insert(Me.grd
Catch ex As Exception
MsgBox("Error in '" & Mid(ex.StackTrace, InStrRev(ex.StackTrace, "\") + 1) & "' - " & ex.Message, MsgBoxStyle.Exclamation)
End Try
End With
End Sub
I'm not saying that it might not be possible. I haven't thought it through. But the end result seems to me to be readily achievable without such thought. And, in my view, doing it in the collection rather than trying to do it via the DataGridView is more consistent with the model with which we're dealing. The DataGridView is really just a Display (and Editing) mechanism for the data which is actually in the collection. If the _user_ has to see, add to, alter, delete such data then it is the DataGridView that provides the necessary user-interface. But if the _program_ needs to do any of those things it has direct access to the collection. Once the program has done any of those things, they do need to be reflected in what the user sees - but the binding copes with that. So, using the program to alter the _datagridview_ is just introducing an unnecessary step in the process. Rather than the program telling the collection and the binding passing that on to the datagridview, the program has to tell the datagridview then the binding has to tell the collection then the binding has to tell the datagridview.
So, as I say, why?
Roger
ASKER
Thanks for your contribution ajaysharmaapjs however after taking a look at them they seem fairly generic and don't take into account data-binding aspects, which is my real issue here.
Roger, I'll give you my reasons for asking this question even though what you are saying makes sense and is the way that I currently have it working.
I'm making a control that has a DataGridView and some buttons (as well as a pop-up menu) to provide functions that add/delete/duplicate/move/ etc. rows. As I plan to use this control in a number of forms (each with slightly different functionality) I thought that the more functionality that is located in the control the less that I need to repeat in the form. So I'm just trying to see whether this approach is possible, even though I'm not sure if it's the best approach to take considering some of the points that you raised.
/Michael
Roger, I'll give you my reasons for asking this question even though what you are saying makes sense and is the way that I currently have it working.
I'm making a control that has a DataGridView and some buttons (as well as a pop-up menu) to provide functions that add/delete/duplicate/move/
/Michael
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Roger,
Considering your comments, and the ones that I already got earlier, it doesn't sound like part of standard functionality so it would not be part of my consideration.
Thanks,
Michael
Considering your comments, and the ones that I already got earlier, it doesn't sound like part of standard functionality so it would not be part of my consideration.
Thanks,
Michael
http://www.extremeexperts.com/Net/CodeSnippets/AddingDataGridItem.aspx