This is my first question here so please be merciful with me.
Purpose:
What I want to accomplish is to allow users to edit rows from a DataGridView (which is binded to a List of objects of custom class) in a Windows Forms application. Also, when a new row is generated in the DataGridView, I need to provide some default values, which I am implementing with the DefaultValuesNeeded event handler from the DataGridView.
Problem: When editing a row, user must be able to navigate outside the DataGridView (for example, to a TextBox to provide extra info), but if the user leaves the new row before editing it, default values dissapear from the row. This is what I need to avoid. If user edits any cell of the new row and then clicks somewhere else in the form, all the values in the row remain there, which is correct and the desired behaviour.
I have created a little project to illustrate this. Form:
Imports System.ComponentModel
Public Class Form1
Private Sub dgvAsientos_DefaultValuesNeeded(sender As Object, e As Windows.Forms.DataGridViewRowEventArgs) Handles DataGridView1.DefaultValuesNeeded
e.Row.Cells("ID").Value = Me.DataGridView1.Rows.Count
e.Row.Cells("Name").Value = "Test Name " & Me.DataGridView1.Rows.Count
e.Row.Cells("Description").Value = "Description " & Me.TextBox1.Text & " " & Me.DataGridView1.Rows.Count
Me.DataGridView1.BindingContext(Me.DataGridView1.DataSource, Me.DataGridView1.DataMember).EndCurrentEdit()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim myList As New BindingList(Of ExampleClass)
For n = 0 To 5
Dim itemn As New ExampleClass
itemn.ID = n
itemn.Name = "Name_" & n
itemn.Description = "Description_" & n
itemn.OptionalField = "OptionalField_" & n
myList.Add(itemn)
Next
Dim bs As New BindingSource()
bs.DataSource = myList
Me.DataGridView1.DataSource = bs
End Sub
End Class
Example class:
Public Class ExampleClass
Public Property ID As Integer
Public Property Name As String
Public Property Description As String
Public Property OptionalField As String
End Class
Any help will be appreciated. I have found very little info regarding DefaultValuesNeeded BindingSources values lost when user focusing some other control; some of them made me add the line following line, but I didn't find that made any difference.
(...).EndCurrentEdit()
I also found suggestions to add a handler for the binding source AddingNew event which returned an instance of the object with the default values I need, again no difference.
Private Sub myBindingSource_AddingNew(sender As Object, e As AddingNewEventArgs)
e.NewObject = CreateNewExample()
End Sub
I hope the question and the format is correct. Thanks in advance, MBD
CodePudding user response:
When the user adds a new row in DataGridView by navigating to the last row and then leave the row before editing the cells; in this case, adding the row will be cancelled. It's because of some internal logic which cancels the new row when the row is not dirty.
To change this behavior, you can handle RowValidating or RowLeave events, then notify the current cell is dirty, and then end the current edit.
C# Example
private void dgv1_RowLeave(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex == dgv1.NewRowIndex)
{
dgv1.NotifyCurrentCellDirty(true);
dgv1.BindingContext[dgv1.DataSource, dgv1.DataMember].EndCurrentEdit();
dgv1.NotifyCurrentCellDirty(false);
}
}
VB.NET Example
Private Sub dgv1_RowLeave(sender As Object, e As DataGridViewCellEventArgs) _
Handles dgv1.RowLeave
If e.RowIndex = dgv1.NewRowIndex Then
dgv1.NotifyCurrentCellDirty(True)
dgv1.BindingContext(dgv1.DataSource, dgv1.DataMember).EndCurrentEdit()
dgv1.NotifyCurrentCellDirty(False)
End If
End Sub